fix: single_range_in_vec_init wrongly unmangles macros

This commit is contained in:
Linshu Yang 2025-11-07 15:48:34 +00:00
parent c8885d5313
commit 6834ab61b1
5 changed files with 212 additions and 21 deletions

View file

@ -1,12 +1,12 @@
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;
@ -87,20 +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)
&& inner_expr.span.is_desugaring(DesugaringKind::RangeExpr)
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, &[])
{
@ -131,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::<std::vec::Vec<{ty}>>()"),
Applicability::MaybeIncorrect,
applicability,
);
}
@ -140,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,
);
}
},

View file

@ -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<T: PartialOrd>(start: T, end: T) {
[start..end];
}
fn awa_vec<T: PartialOrd>(start: T, end: T) {
vec![start..end];
}
fn main() {
// Lint
(0..200).collect::<std::vec::Vec<i32>>();
//~^ single_range_in_vec_init
(0..200).collect::<std::vec::Vec<i32>>();
//~^ single_range_in_vec_init
(0u8..200).collect::<std::vec::Vec<u8>>();
//~^ single_range_in_vec_init
(0usize..200).collect::<std::vec::Vec<usize>>();
//~^ single_range_in_vec_init
(0..200usize).collect::<std::vec::Vec<usize>>();
//~^ single_range_in_vec_init
(0u8..200).collect::<std::vec::Vec<u8>>();
//~^ single_range_in_vec_init
(0usize..200).collect::<std::vec::Vec<usize>>();
//~^ single_range_in_vec_init
(0..200usize).collect::<std::vec::Vec<usize>>();
//~^ single_range_in_vec_init
// Only suggest collect
(0..200isize).collect::<std::vec::Vec<isize>>();
//~^ single_range_in_vec_init
(0..200isize).collect::<std::vec::Vec<isize>>();
//~^ 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::<std::vec::Vec<i32>>();
//~^ single_range_in_vec_init
}

View file

@ -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<T: PartialOrd>(start: T, end: T) {
[start..end];
}
fn awa_vec<T: PartialOrd>(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::<std::vec::Vec<isize>>();
//~^ single_range_in_vec_init
(0..200isize).collect::<std::vec::Vec<isize>>();
//~^ 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::<std::vec::Vec<i32>>();
//~^ single_range_in_vec_init
}

View file

@ -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)]
@ -72,3 +71,14 @@ fn issue16042() {
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
}

View file

@ -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::<std::vec::Vec<isize>>();
|
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::<std::vec::Vec<isize>>();
|
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::<std::vec::Vec<i32>>();
|
error: aborting due to 11 previous errors