Skip to content

Commit 6834ab6

Browse files
committed
fix: single_range_in_vec_init wrongly unmangles macros
1 parent c8885d5 commit 6834ab6

File tree

5 files changed

+212
-21
lines changed

5 files changed

+212
-21
lines changed

clippy_lints/src/single_range_in_vec_init.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::higher::VecArgs;
33
use clippy_utils::macros::root_macro_call_first_node;
4-
use clippy_utils::source::SpanRangeExt;
4+
use clippy_utils::source::{SpanRangeExt, snippet_with_context};
55
use clippy_utils::ty::implements_trait;
66
use clippy_utils::{is_no_std_crate, sym};
77
use rustc_ast::{LitIntType, LitKind, UintTy};
88
use rustc_errors::Applicability;
9-
use rustc_hir::{Expr, ExprKind, LangItem, StructTailExpr};
9+
use rustc_hir::{Expr, ExprKind, StructTailExpr};
1010
use rustc_lint::{LateContext, LateLintPass};
1111
use rustc_session::declare_lint_pass;
1212
use rustc_span::DesugaringKind;
@@ -87,20 +87,21 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
8787
return;
8888
};
8989

90-
let ExprKind::Struct(&qpath, [start, end], StructTailExpr::None) = inner_expr.kind else {
90+
let ExprKind::Struct(_, [start, end], StructTailExpr::None) = inner_expr.kind else {
9191
return;
9292
};
9393

94-
if cx.tcx.qpath_is_lang_item(qpath, LangItem::Range)
95-
&& inner_expr.span.is_desugaring(DesugaringKind::RangeExpr)
94+
if inner_expr.span.is_desugaring(DesugaringKind::RangeExpr)
9695
&& let ty = cx.typeck_results().expr_ty(start.expr)
9796
&& let Some(snippet) = span.get_source_text(cx)
9897
// `is_from_proc_macro` will skip any `vec![]`. Let's not!
9998
&& snippet.starts_with(suggested_type.starts_with())
10099
&& snippet.ends_with(suggested_type.ends_with())
101-
&& let Some(start_snippet) = start.span.get_source_text(cx)
102-
&& let Some(end_snippet) = end.span.get_source_text(cx)
103100
{
101+
let mut applicability = Applicability::MachineApplicable;
102+
let (start_snippet, _) = snippet_with_context(cx, start.expr.span, span.ctxt(), "..", &mut applicability);
103+
let (end_snippet, _) = snippet_with_context(cx, end.expr.span, span.ctxt(), "..", &mut applicability);
104+
104105
let should_emit_every_value = if let Some(step_def_id) = cx.tcx.get_diagnostic_item(sym::range_step)
105106
&& implements_trait(cx, ty, step_def_id, &[])
106107
{
@@ -131,7 +132,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
131132
span,
132133
"if you wanted a `Vec` that contains the entire range, try",
133134
format!("({start_snippet}..{end_snippet}).collect::<std::vec::Vec<{ty}>>()"),
134-
Applicability::MaybeIncorrect,
135+
applicability,
135136
);
136137
}
137138

@@ -140,7 +141,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
140141
inner_expr.span,
141142
format!("if you wanted {suggested_type} of len {end_snippet}, try"),
142143
format!("{start_snippet}; {end_snippet}"),
143-
Applicability::MaybeIncorrect,
144+
applicability,
144145
);
145146
}
146147
},
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//@aux-build:proc_macros.rs
2+
#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)]
3+
#![warn(clippy::single_range_in_vec_init)]
4+
5+
#[macro_use]
6+
extern crate proc_macros;
7+
8+
macro_rules! a {
9+
() => {
10+
vec![0..200];
11+
};
12+
}
13+
14+
fn awa<T: PartialOrd>(start: T, end: T) {
15+
[start..end];
16+
}
17+
18+
fn awa_vec<T: PartialOrd>(start: T, end: T) {
19+
vec![start..end];
20+
}
21+
22+
fn main() {
23+
// Lint
24+
(0..200).collect::<std::vec::Vec<i32>>();
25+
//~^ single_range_in_vec_init
26+
(0..200).collect::<std::vec::Vec<i32>>();
27+
//~^ single_range_in_vec_init
28+
(0u8..200).collect::<std::vec::Vec<u8>>();
29+
//~^ single_range_in_vec_init
30+
(0usize..200).collect::<std::vec::Vec<usize>>();
31+
//~^ single_range_in_vec_init
32+
(0..200usize).collect::<std::vec::Vec<usize>>();
33+
//~^ single_range_in_vec_init
34+
(0u8..200).collect::<std::vec::Vec<u8>>();
35+
//~^ single_range_in_vec_init
36+
(0usize..200).collect::<std::vec::Vec<usize>>();
37+
//~^ single_range_in_vec_init
38+
(0..200usize).collect::<std::vec::Vec<usize>>();
39+
//~^ single_range_in_vec_init
40+
// Only suggest collect
41+
(0..200isize).collect::<std::vec::Vec<isize>>();
42+
//~^ single_range_in_vec_init
43+
(0..200isize).collect::<std::vec::Vec<isize>>();
44+
//~^ single_range_in_vec_init
45+
// Do not lint
46+
[0..200, 0..100];
47+
vec![0..200, 0..100];
48+
[0.0..200.0];
49+
vec![0.0..200.0];
50+
// `Copy` is not implemented for `Range`, so this doesn't matter
51+
// FIXME: [0..200; 2];
52+
// FIXME: [vec!0..200; 2];
53+
54+
// Unfortunately skips any macros
55+
a!();
56+
57+
// Skip external macros and procedural macros
58+
external! {
59+
[0..200];
60+
vec![0..200];
61+
}
62+
with_span! {
63+
span
64+
[0..200];
65+
vec![0..200];
66+
}
67+
}
68+
69+
fn issue16042() {
70+
use std::ops::Range;
71+
72+
let input = vec![Range { start: 0, end: 5 }];
73+
}
74+
75+
fn issue16044() {
76+
macro_rules! as_i32 {
77+
($x:expr) => {
78+
$x as i32
79+
};
80+
}
81+
82+
let input = (0..as_i32!(10)).collect::<std::vec::Vec<i32>>();
83+
//~^ single_range_in_vec_init
84+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//@aux-build:proc_macros.rs
2+
#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)]
3+
#![warn(clippy::single_range_in_vec_init)]
4+
5+
#[macro_use]
6+
extern crate proc_macros;
7+
8+
macro_rules! a {
9+
() => {
10+
vec![0..200];
11+
};
12+
}
13+
14+
fn awa<T: PartialOrd>(start: T, end: T) {
15+
[start..end];
16+
}
17+
18+
fn awa_vec<T: PartialOrd>(start: T, end: T) {
19+
vec![start..end];
20+
}
21+
22+
fn main() {
23+
// Lint
24+
[0; 200];
25+
//~^ single_range_in_vec_init
26+
vec![0; 200];
27+
//~^ single_range_in_vec_init
28+
[0u8; 200];
29+
//~^ single_range_in_vec_init
30+
[0usize; 200];
31+
//~^ single_range_in_vec_init
32+
[0; 200usize];
33+
//~^ single_range_in_vec_init
34+
vec![0u8; 200];
35+
//~^ single_range_in_vec_init
36+
vec![0usize; 200];
37+
//~^ single_range_in_vec_init
38+
vec![0; 200usize];
39+
//~^ single_range_in_vec_init
40+
// Only suggest collect
41+
(0..200isize).collect::<std::vec::Vec<isize>>();
42+
//~^ single_range_in_vec_init
43+
(0..200isize).collect::<std::vec::Vec<isize>>();
44+
//~^ single_range_in_vec_init
45+
// Do not lint
46+
[0..200, 0..100];
47+
vec![0..200, 0..100];
48+
[0.0..200.0];
49+
vec![0.0..200.0];
50+
// `Copy` is not implemented for `Range`, so this doesn't matter
51+
// FIXME: [0..200; 2];
52+
// FIXME: [vec!0..200; 2];
53+
54+
// Unfortunately skips any macros
55+
a!();
56+
57+
// Skip external macros and procedural macros
58+
external! {
59+
[0..200];
60+
vec![0..200];
61+
}
62+
with_span! {
63+
span
64+
[0..200];
65+
vec![0..200];
66+
}
67+
}
68+
69+
fn issue16042() {
70+
use std::ops::Range;
71+
72+
let input = vec![Range { start: 0, end: 5 }];
73+
}
74+
75+
fn issue16044() {
76+
macro_rules! as_i32 {
77+
($x:expr) => {
78+
$x as i32
79+
};
80+
}
81+
82+
let input = (0..as_i32!(10)).collect::<std::vec::Vec<i32>>();
83+
//~^ single_range_in_vec_init
84+
}

tests/ui/single_range_in_vec_init.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//@aux-build:proc_macros.rs
2-
//@no-rustfix: overlapping suggestions
32
#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::useless_vec, unused)]
43
#![warn(clippy::single_range_in_vec_init)]
54

@@ -72,3 +71,14 @@ fn issue16042() {
7271

7372
let input = vec![Range { start: 0, end: 5 }];
7473
}
74+
75+
fn issue16044() {
76+
macro_rules! as_i32 {
77+
($x:expr) => {
78+
$x as i32
79+
};
80+
}
81+
82+
let input = vec![0..as_i32!(10)];
83+
//~^ single_range_in_vec_init
84+
}

tests/ui/single_range_in_vec_init.stderr

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: an array of `Range` that is only one element
2-
--> tests/ui/single_range_in_vec_init.rs:25:5
2+
--> tests/ui/single_range_in_vec_init.rs:24:5
33
|
44
LL | [0..200];
55
| ^^^^^^^^
@@ -18,7 +18,7 @@ LL + [0; 200];
1818
|
1919

2020
error: a `Vec` of `Range` that is only one element
21-
--> tests/ui/single_range_in_vec_init.rs:27:5
21+
--> tests/ui/single_range_in_vec_init.rs:26:5
2222
|
2323
LL | vec![0..200];
2424
| ^^^^^^^^^^^^
@@ -35,7 +35,7 @@ LL + vec![0; 200];
3535
|
3636

3737
error: an array of `Range` that is only one element
38-
--> tests/ui/single_range_in_vec_init.rs:29:5
38+
--> tests/ui/single_range_in_vec_init.rs:28:5
3939
|
4040
LL | [0u8..200];
4141
| ^^^^^^^^^^
@@ -52,7 +52,7 @@ LL + [0u8; 200];
5252
|
5353

5454
error: an array of `Range` that is only one element
55-
--> tests/ui/single_range_in_vec_init.rs:31:5
55+
--> tests/ui/single_range_in_vec_init.rs:30:5
5656
|
5757
LL | [0usize..200];
5858
| ^^^^^^^^^^^^^
@@ -69,7 +69,7 @@ LL + [0usize; 200];
6969
|
7070

7171
error: an array of `Range` that is only one element
72-
--> tests/ui/single_range_in_vec_init.rs:33:5
72+
--> tests/ui/single_range_in_vec_init.rs:32:5
7373
|
7474
LL | [0..200usize];
7575
| ^^^^^^^^^^^^^
@@ -86,7 +86,7 @@ LL + [0; 200usize];
8686
|
8787

8888
error: a `Vec` of `Range` that is only one element
89-
--> tests/ui/single_range_in_vec_init.rs:35:5
89+
--> tests/ui/single_range_in_vec_init.rs:34:5
9090
|
9191
LL | vec![0u8..200];
9292
| ^^^^^^^^^^^^^^
@@ -103,7 +103,7 @@ LL + vec![0u8; 200];
103103
|
104104

105105
error: a `Vec` of `Range` that is only one element
106-
--> tests/ui/single_range_in_vec_init.rs:37:5
106+
--> tests/ui/single_range_in_vec_init.rs:36:5
107107
|
108108
LL | vec![0usize..200];
109109
| ^^^^^^^^^^^^^^^^^
@@ -120,7 +120,7 @@ LL + vec![0usize; 200];
120120
|
121121

122122
error: a `Vec` of `Range` that is only one element
123-
--> tests/ui/single_range_in_vec_init.rs:39:5
123+
--> tests/ui/single_range_in_vec_init.rs:38:5
124124
|
125125
LL | vec![0..200usize];
126126
| ^^^^^^^^^^^^^^^^^
@@ -137,7 +137,7 @@ LL + vec![0; 200usize];
137137
|
138138

139139
error: an array of `Range` that is only one element
140-
--> tests/ui/single_range_in_vec_init.rs:42:5
140+
--> tests/ui/single_range_in_vec_init.rs:41:5
141141
|
142142
LL | [0..200isize];
143143
| ^^^^^^^^^^^^^
@@ -149,7 +149,7 @@ LL + (0..200isize).collect::<std::vec::Vec<isize>>();
149149
|
150150

151151
error: a `Vec` of `Range` that is only one element
152-
--> tests/ui/single_range_in_vec_init.rs:44:5
152+
--> tests/ui/single_range_in_vec_init.rs:43:5
153153
|
154154
LL | vec![0..200isize];
155155
| ^^^^^^^^^^^^^^^^^
@@ -160,5 +160,17 @@ LL - vec![0..200isize];
160160
LL + (0..200isize).collect::<std::vec::Vec<isize>>();
161161
|
162162

163-
error: aborting due to 10 previous errors
163+
error: a `Vec` of `Range` that is only one element
164+
--> tests/ui/single_range_in_vec_init.rs:82:17
165+
|
166+
LL | let input = vec![0..as_i32!(10)];
167+
| ^^^^^^^^^^^^^^^^^^^^
168+
|
169+
help: if you wanted a `Vec` that contains the entire range, try
170+
|
171+
LL - let input = vec![0..as_i32!(10)];
172+
LL + let input = (0..as_i32!(10)).collect::<std::vec::Vec<i32>>();
173+
|
174+
175+
error: aborting due to 11 previous errors
164176

0 commit comments

Comments
 (0)