diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs index 412ca2fa4ed9..92d1b112198f 100644 --- a/clippy_lints/src/single_range_in_vec_init.rs +++ b/clippy_lints/src/single_range_in_vec_init.rs @@ -1,14 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::ty::implements_trait; use clippy_utils::{is_no_std_crate, sym}; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, StructTailExpr}; +use rustc_hir::{Expr, ExprKind, StructTailExpr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::DesugaringKind; use std::fmt::{self, Display, Formatter}; declare_clippy_lint! { @@ -86,19 +87,21 @@ impl LateLintPass<'_> for SingleRangeInVecInit { return; }; - let ExprKind::Struct(&qpath, [start, end], StructTailExpr::None) = inner_expr.kind else { + let ExprKind::Struct(_, [start, end], StructTailExpr::None) = inner_expr.kind else { return; }; - if cx.tcx.qpath_is_lang_item(qpath, LangItem::Range) + if inner_expr.span.is_desugaring(DesugaringKind::RangeExpr) && let ty = cx.typeck_results().expr_ty(start.expr) && let Some(snippet) = span.get_source_text(cx) // `is_from_proc_macro` will skip any `vec![]`. Let's not! && snippet.starts_with(suggested_type.starts_with()) && snippet.ends_with(suggested_type.ends_with()) - && let Some(start_snippet) = start.span.get_source_text(cx) - && let Some(end_snippet) = end.span.get_source_text(cx) { + let mut applicability = Applicability::MachineApplicable; + let (start_snippet, _) = snippet_with_context(cx, start.expr.span, span.ctxt(), "..", &mut applicability); + let (end_snippet, _) = snippet_with_context(cx, end.expr.span, span.ctxt(), "..", &mut applicability); + let should_emit_every_value = if let Some(step_def_id) = cx.tcx.get_diagnostic_item(sym::range_step) && implements_trait(cx, ty, step_def_id, &[]) { @@ -129,7 +132,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { span, "if you wanted a `Vec` that contains the entire range, try", format!("({start_snippet}..{end_snippet}).collect::>()"), - Applicability::MaybeIncorrect, + applicability, ); } @@ -138,7 +141,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { inner_expr.span, format!("if you wanted {suggested_type} of len {end_snippet}, try"), format!("{start_snippet}; {end_snippet}"), - Applicability::MaybeIncorrect, + applicability, ); } }, diff --git a/tests/ui/single_range_in_vec_init.1.fixed b/tests/ui/single_range_in_vec_init.1.fixed new file mode 100644 index 000000000000..0af91907ad05 --- /dev/null +++ b/tests/ui/single_range_in_vec_init.1.fixed @@ -0,0 +1,84 @@ +//@aux-build:proc_macros.rs +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)] +#![warn(clippy::single_range_in_vec_init)] + +#[macro_use] +extern crate proc_macros; + +macro_rules! a { + () => { + vec![0..200]; + }; +} + +fn awa(start: T, end: T) { + [start..end]; +} + +fn awa_vec(start: T, end: T) { + vec![start..end]; +} + +fn main() { + // Lint + (0..200).collect::>(); + //~^ single_range_in_vec_init + (0..200).collect::>(); + //~^ single_range_in_vec_init + (0u8..200).collect::>(); + //~^ single_range_in_vec_init + (0usize..200).collect::>(); + //~^ single_range_in_vec_init + (0..200usize).collect::>(); + //~^ single_range_in_vec_init + (0u8..200).collect::>(); + //~^ single_range_in_vec_init + (0usize..200).collect::>(); + //~^ single_range_in_vec_init + (0..200usize).collect::>(); + //~^ single_range_in_vec_init + // Only suggest collect + (0..200isize).collect::>(); + //~^ single_range_in_vec_init + (0..200isize).collect::>(); + //~^ single_range_in_vec_init + // Do not lint + [0..200, 0..100]; + vec![0..200, 0..100]; + [0.0..200.0]; + vec![0.0..200.0]; + // `Copy` is not implemented for `Range`, so this doesn't matter + // FIXME: [0..200; 2]; + // FIXME: [vec!0..200; 2]; + + // Unfortunately skips any macros + a!(); + + // Skip external macros and procedural macros + external! { + [0..200]; + vec![0..200]; + } + with_span! { + span + [0..200]; + vec![0..200]; + } +} + +fn issue16042() { + use std::ops::Range; + + let input = vec![Range { start: 0, end: 5 }]; +} + +fn issue16044() { + macro_rules! as_i32 { + ($x:expr) => { + $x as i32 + }; + } + + let input = (0..as_i32!(10)).collect::>(); + //~^ single_range_in_vec_init +} diff --git a/tests/ui/single_range_in_vec_init.2.fixed b/tests/ui/single_range_in_vec_init.2.fixed new file mode 100644 index 000000000000..fd6b91360aeb --- /dev/null +++ b/tests/ui/single_range_in_vec_init.2.fixed @@ -0,0 +1,84 @@ +//@aux-build:proc_macros.rs +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)] +#![warn(clippy::single_range_in_vec_init)] + +#[macro_use] +extern crate proc_macros; + +macro_rules! a { + () => { + vec![0..200]; + }; +} + +fn awa(start: T, end: T) { + [start..end]; +} + +fn awa_vec(start: T, end: T) { + vec![start..end]; +} + +fn main() { + // Lint + [0; 200]; + //~^ single_range_in_vec_init + vec![0; 200]; + //~^ single_range_in_vec_init + [0u8; 200]; + //~^ single_range_in_vec_init + [0usize; 200]; + //~^ single_range_in_vec_init + [0; 200usize]; + //~^ single_range_in_vec_init + vec![0u8; 200]; + //~^ single_range_in_vec_init + vec![0usize; 200]; + //~^ single_range_in_vec_init + vec![0; 200usize]; + //~^ single_range_in_vec_init + // Only suggest collect + (0..200isize).collect::>(); + //~^ single_range_in_vec_init + (0..200isize).collect::>(); + //~^ single_range_in_vec_init + // Do not lint + [0..200, 0..100]; + vec![0..200, 0..100]; + [0.0..200.0]; + vec![0.0..200.0]; + // `Copy` is not implemented for `Range`, so this doesn't matter + // FIXME: [0..200; 2]; + // FIXME: [vec!0..200; 2]; + + // Unfortunately skips any macros + a!(); + + // Skip external macros and procedural macros + external! { + [0..200]; + vec![0..200]; + } + with_span! { + span + [0..200]; + vec![0..200]; + } +} + +fn issue16042() { + use std::ops::Range; + + let input = vec![Range { start: 0, end: 5 }]; +} + +fn issue16044() { + macro_rules! as_i32 { + ($x:expr) => { + $x as i32 + }; + } + + let input = (0..as_i32!(10)).collect::>(); + //~^ single_range_in_vec_init +} diff --git a/tests/ui/single_range_in_vec_init.rs b/tests/ui/single_range_in_vec_init.rs index 0888019e101c..1cc2b894c034 100644 --- a/tests/ui/single_range_in_vec_init.rs +++ b/tests/ui/single_range_in_vec_init.rs @@ -1,5 +1,4 @@ //@aux-build:proc_macros.rs -//@no-rustfix: overlapping suggestions #![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)] #![warn(clippy::single_range_in_vec_init)] @@ -66,3 +65,20 @@ fn main() { vec![0..200]; } } + +fn issue16042() { + use std::ops::Range; + + let input = vec![Range { start: 0, end: 5 }]; +} + +fn issue16044() { + macro_rules! as_i32 { + ($x:expr) => { + $x as i32 + }; + } + + let input = vec![0..as_i32!(10)]; + //~^ single_range_in_vec_init +} diff --git a/tests/ui/single_range_in_vec_init.stderr b/tests/ui/single_range_in_vec_init.stderr index b21338e38a3c..d93379777d39 100644 --- a/tests/ui/single_range_in_vec_init.stderr +++ b/tests/ui/single_range_in_vec_init.stderr @@ -1,5 +1,5 @@ error: an array of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:25:5 + --> tests/ui/single_range_in_vec_init.rs:24:5 | LL | [0..200]; | ^^^^^^^^ @@ -18,7 +18,7 @@ LL + [0; 200]; | error: a `Vec` of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:27:5 + --> tests/ui/single_range_in_vec_init.rs:26:5 | LL | vec![0..200]; | ^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL + vec![0; 200]; | error: an array of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:29:5 + --> tests/ui/single_range_in_vec_init.rs:28:5 | LL | [0u8..200]; | ^^^^^^^^^^ @@ -52,7 +52,7 @@ LL + [0u8; 200]; | error: an array of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:31:5 + --> tests/ui/single_range_in_vec_init.rs:30:5 | LL | [0usize..200]; | ^^^^^^^^^^^^^ @@ -69,7 +69,7 @@ LL + [0usize; 200]; | error: an array of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:33:5 + --> tests/ui/single_range_in_vec_init.rs:32:5 | LL | [0..200usize]; | ^^^^^^^^^^^^^ @@ -86,7 +86,7 @@ LL + [0; 200usize]; | error: a `Vec` of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:35:5 + --> tests/ui/single_range_in_vec_init.rs:34:5 | LL | vec![0u8..200]; | ^^^^^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + vec![0u8; 200]; | error: a `Vec` of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:37:5 + --> tests/ui/single_range_in_vec_init.rs:36:5 | LL | vec![0usize..200]; | ^^^^^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + vec![0usize; 200]; | error: a `Vec` of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:39:5 + --> tests/ui/single_range_in_vec_init.rs:38:5 | LL | vec![0..200usize]; | ^^^^^^^^^^^^^^^^^ @@ -137,7 +137,7 @@ LL + vec![0; 200usize]; | error: an array of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:42:5 + --> tests/ui/single_range_in_vec_init.rs:41:5 | LL | [0..200isize]; | ^^^^^^^^^^^^^ @@ -149,7 +149,7 @@ LL + (0..200isize).collect::>(); | error: a `Vec` of `Range` that is only one element - --> tests/ui/single_range_in_vec_init.rs:44:5 + --> tests/ui/single_range_in_vec_init.rs:43:5 | LL | vec![0..200isize]; | ^^^^^^^^^^^^^^^^^ @@ -160,5 +160,17 @@ LL - vec![0..200isize]; LL + (0..200isize).collect::>(); | -error: aborting due to 10 previous errors +error: a `Vec` of `Range` that is only one element + --> tests/ui/single_range_in_vec_init.rs:82:17 + | +LL | let input = vec![0..as_i32!(10)]; + | ^^^^^^^^^^^^^^^^^^^^ + | +help: if you wanted a `Vec` that contains the entire range, try + | +LL - let input = vec![0..as_i32!(10)]; +LL + let input = (0..as_i32!(10)).collect::>(); + | + +error: aborting due to 11 previous errors