Skip to content

Commit 038dd12

Browse files
committed
Suggest add bounding value for RangeTo
1 parent b2ee1b3 commit 038dd12

File tree

3 files changed

+207
-0
lines changed

3 files changed

+207
-0
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10771077
}
10781078

10791079
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected);
1080+
self.suggest_bounds_for_range_to_method(&mut err, source, item_ident);
10801081
err.emit()
10811082
}
10821083

@@ -3281,6 +3282,80 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
32813282
}
32823283
}
32833284

3285+
fn suggest_bounds_for_range_to_method(
3286+
&self,
3287+
err: &mut Diag<'_>,
3288+
source: SelfSource<'tcx>,
3289+
item_ident: Ident,
3290+
) {
3291+
if let SelfSource::MethodCall(rcvr_expr) = source
3292+
&& let hir::ExprKind::Struct(qpath, fields, _) = rcvr_expr.kind
3293+
&& let hir::QPath::LangItem(lang_item, _) = qpath
3294+
{
3295+
let is_inclusive = match lang_item {
3296+
hir::LangItem::RangeTo => false,
3297+
hir::LangItem::RangeToInclusive | hir::LangItem::RangeInclusiveCopy => true,
3298+
_ => return,
3299+
};
3300+
3301+
let tcx = self.tcx;
3302+
if let Some(iterator_trait) = tcx.get_diagnostic_item(sym::Iterator) {
3303+
let has_method = tcx
3304+
.associated_items(iterator_trait)
3305+
.filter_by_name_unhygienic(item_ident.name)
3306+
.next()
3307+
.is_some();
3308+
3309+
if !has_method {
3310+
return;
3311+
}
3312+
}
3313+
3314+
let element_ty = self.typeck_results.borrow().expr_ty_opt(rcvr_expr).and_then(|ty| {
3315+
if let ty::Adt(_, args) = ty.kind() {
3316+
args.get(0).and_then(|arg| arg.as_type())
3317+
} else {
3318+
None
3319+
}
3320+
});
3321+
3322+
let is_integral = element_ty.is_some_and(|ty| ty.is_integral());
3323+
let source_map = self.tcx.sess.source_map();
3324+
let range_type = if is_inclusive { "RangeInclusive" } else { "Range" };
3325+
3326+
// Check if the end bound is a negative number
3327+
let end_is_negative = is_integral
3328+
&& fields.iter().find(|f| f.ident.name == rustc_span::sym::end).is_some_and(
3329+
|end_field| {
3330+
matches!(end_field.expr.kind, hir::ExprKind::Unary(rustc_ast::UnOp::Neg, _))
3331+
},
3332+
);
3333+
3334+
if let Ok(snippet) = source_map.span_to_snippet(rcvr_expr.span) {
3335+
let offset = snippet
3336+
.chars()
3337+
.take_while(|&c| c == '(' || c.is_whitespace())
3338+
.map(|c| c.len_utf8())
3339+
.sum::<usize>();
3340+
3341+
let insert_span = rcvr_expr
3342+
.span
3343+
.with_lo(rcvr_expr.span.lo() + rustc_span::BytePos(offset as u32))
3344+
.shrink_to_lo();
3345+
let span_value = if is_integral && !end_is_negative { "0" } else { "/* start */" };
3346+
3347+
err.span_suggestion_verbose(
3348+
insert_span,
3349+
format!(
3350+
"consider using a bounded `{range_type}` by adding a concrete starting value"
3351+
),
3352+
span_value,
3353+
Applicability::HasPlaceholders,
3354+
);
3355+
}
3356+
}
3357+
}
3358+
32843359
/// Print out the type for use in value namespace.
32853360
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
32863361
match ty.kind() {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
fn main() {
2+
for x in (..4).rev() {
3+
//~^ ERROR `RangeTo<{integer}>` is not an iterator
4+
//~| HELP consider using a bounded `Range` by adding a concrete starting value
5+
let _ = x;
6+
}
7+
8+
for x in (..=4).rev() {
9+
//~^ ERROR `std::ops::RangeToInclusive<{integer}>` is not an iterator
10+
//~| HELP consider using a bounded `RangeInclusive` by adding a concrete starting value
11+
let _ = x;
12+
}
13+
14+
// should not suggest for `iter` method
15+
let _v: Vec<_> = (..5).iter().collect();
16+
//~^ ERROR no method named `iter` found
17+
18+
for _x in (..'a').rev() {}
19+
//~^ ERROR `RangeTo<char>` is not an iterator
20+
//~| HELP consider using a bounded `Range` by adding a concrete starting value
21+
22+
for _x in (..='a').rev() {}
23+
//~^ ERROR `std::ops::RangeToInclusive<char>` is not an iterator
24+
//~| HELP consider using a bounded `RangeInclusive` by adding a concrete starting value
25+
26+
for _x in (..-10).rev() {}
27+
//~^ ERROR `RangeTo<{integer}>` is not an iterator
28+
//~| HELP consider using a bounded `Range` by adding a concrete starting value
29+
30+
for _x in (..=-10).rev() {}
31+
//~^ ERROR `std::ops::RangeToInclusive<{integer}>` is not an iterator
32+
//~| HELP consider using a bounded `RangeInclusive` by adding a concrete starting value
33+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
error[E0599]: `RangeTo<{integer}>` is not an iterator
2+
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:2:20
3+
|
4+
LL | for x in (..4).rev() {
5+
| ^^^ `RangeTo<{integer}>` is not an iterator
6+
|
7+
= note: the following trait bounds were not satisfied:
8+
`RangeTo<{integer}>: Iterator`
9+
which is required by `&mut RangeTo<{integer}>: Iterator`
10+
= note: you might have meant to use a bounded `Range`
11+
help: consider using a bounded `Range` by adding a concrete starting value
12+
|
13+
LL | for x in (0..4).rev() {
14+
| +
15+
16+
error[E0599]: `std::ops::RangeToInclusive<{integer}>` is not an iterator
17+
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:8:21
18+
|
19+
LL | for x in (..=4).rev() {
20+
| ^^^ `std::ops::RangeToInclusive<{integer}>` is not an iterator
21+
|
22+
= note: the following trait bounds were not satisfied:
23+
`std::ops::RangeToInclusive<{integer}>: Iterator`
24+
which is required by `&mut std::ops::RangeToInclusive<{integer}>: Iterator`
25+
= note: you might have meant to use a bounded `RangeInclusive`
26+
help: consider using a bounded `RangeInclusive` by adding a concrete starting value
27+
|
28+
LL | for x in (0..=4).rev() {
29+
| +
30+
31+
error[E0599]: no method named `iter` found for struct `RangeTo<Idx>` in the current scope
32+
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:15:28
33+
|
34+
LL | let _v: Vec<_> = (..5).iter().collect();
35+
| ^^^^ method not found in `RangeTo<{integer}>`
36+
37+
error[E0599]: `RangeTo<char>` is not an iterator
38+
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:18:23
39+
|
40+
LL | for _x in (..'a').rev() {}
41+
| ^^^ `RangeTo<char>` is not an iterator
42+
|
43+
= note: the following trait bounds were not satisfied:
44+
`RangeTo<char>: Iterator`
45+
which is required by `&mut RangeTo<char>: Iterator`
46+
= note: you might have meant to use a bounded `Range`
47+
help: consider using a bounded `Range` by adding a concrete starting value
48+
|
49+
LL | for _x in (/* start */..'a').rev() {}
50+
| +++++++++++
51+
52+
error[E0599]: `std::ops::RangeToInclusive<char>` is not an iterator
53+
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:22:24
54+
|
55+
LL | for _x in (..='a').rev() {}
56+
| ^^^ `std::ops::RangeToInclusive<char>` is not an iterator
57+
|
58+
= note: the following trait bounds were not satisfied:
59+
`std::ops::RangeToInclusive<char>: Iterator`
60+
which is required by `&mut std::ops::RangeToInclusive<char>: Iterator`
61+
= note: you might have meant to use a bounded `RangeInclusive`
62+
help: consider using a bounded `RangeInclusive` by adding a concrete starting value
63+
|
64+
LL | for _x in (/* start */..='a').rev() {}
65+
| +++++++++++
66+
67+
error[E0599]: `RangeTo<{integer}>` is not an iterator
68+
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:26:23
69+
|
70+
LL | for _x in (..-10).rev() {}
71+
| ^^^ `RangeTo<{integer}>` is not an iterator
72+
|
73+
= note: the following trait bounds were not satisfied:
74+
`RangeTo<{integer}>: Iterator`
75+
which is required by `&mut RangeTo<{integer}>: Iterator`
76+
= note: you might have meant to use a bounded `Range`
77+
help: consider using a bounded `Range` by adding a concrete starting value
78+
|
79+
LL | for _x in (/* start */..-10).rev() {}
80+
| +++++++++++
81+
82+
error[E0599]: `std::ops::RangeToInclusive<{integer}>` is not an iterator
83+
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:30:24
84+
|
85+
LL | for _x in (..=-10).rev() {}
86+
| ^^^ `std::ops::RangeToInclusive<{integer}>` is not an iterator
87+
|
88+
= note: the following trait bounds were not satisfied:
89+
`std::ops::RangeToInclusive<{integer}>: Iterator`
90+
which is required by `&mut std::ops::RangeToInclusive<{integer}>: Iterator`
91+
= note: you might have meant to use a bounded `RangeInclusive`
92+
help: consider using a bounded `RangeInclusive` by adding a concrete starting value
93+
|
94+
LL | for _x in (/* start */..=-10).rev() {}
95+
| +++++++++++
96+
97+
error: aborting due to 7 previous errors
98+
99+
For more information about this error, try `rustc --explain E0599`.

0 commit comments

Comments
 (0)