Skip to content

Commit 1a2f058

Browse files
committed
Suggest add bounding value for RangeTo
1 parent 235a4c0 commit 1a2f058

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
@@ -1732,6 +1732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17321732
}
17331733

17341734
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected);
1735+
self.suggest_bounds_for_range_to_method(&mut err, source, item_ident);
17351736
err.emit()
17361737
}
17371738

@@ -3228,6 +3229,80 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
32283229
}
32293230
}
32303231

3232+
fn suggest_bounds_for_range_to_method(
3233+
&self,
3234+
err: &mut Diag<'_>,
3235+
source: SelfSource<'tcx>,
3236+
item_ident: Ident,
3237+
) {
3238+
if let SelfSource::MethodCall(rcvr_expr) = source
3239+
&& let hir::ExprKind::Struct(qpath, fields, _) = rcvr_expr.kind
3240+
&& let hir::QPath::LangItem(lang_item, _) = qpath
3241+
{
3242+
let is_inclusive = match lang_item {
3243+
hir::LangItem::RangeTo => false,
3244+
hir::LangItem::RangeToInclusive | hir::LangItem::RangeInclusiveCopy => true,
3245+
_ => return,
3246+
};
3247+
3248+
let tcx = self.tcx;
3249+
if let Some(iterator_trait) = tcx.get_diagnostic_item(sym::Iterator) {
3250+
let has_method = tcx
3251+
.associated_items(iterator_trait)
3252+
.filter_by_name_unhygienic(item_ident.name)
3253+
.next()
3254+
.is_some();
3255+
3256+
if !has_method {
3257+
return;
3258+
}
3259+
}
3260+
3261+
let element_ty = self.typeck_results.borrow().expr_ty_opt(rcvr_expr).and_then(|ty| {
3262+
if let ty::Adt(_, args) = ty.kind() {
3263+
args.get(0).and_then(|arg| arg.as_type())
3264+
} else {
3265+
None
3266+
}
3267+
});
3268+
3269+
let is_integral = element_ty.is_some_and(|ty| ty.is_integral());
3270+
let source_map = self.tcx.sess.source_map();
3271+
let range_type = if is_inclusive { "RangeInclusive" } else { "Range" };
3272+
3273+
// Check if the end bound is a negative number
3274+
let end_is_negative = is_integral
3275+
&& fields.iter().find(|f| f.ident.name == rustc_span::sym::end).is_some_and(
3276+
|end_field| {
3277+
matches!(end_field.expr.kind, hir::ExprKind::Unary(rustc_ast::UnOp::Neg, _))
3278+
},
3279+
);
3280+
3281+
if let Ok(snippet) = source_map.span_to_snippet(rcvr_expr.span) {
3282+
let offset = snippet
3283+
.chars()
3284+
.take_while(|&c| c == '(' || c.is_whitespace())
3285+
.map(|c| c.len_utf8())
3286+
.sum::<usize>();
3287+
3288+
let insert_span = rcvr_expr
3289+
.span
3290+
.with_lo(rcvr_expr.span.lo() + rustc_span::BytePos(offset as u32))
3291+
.shrink_to_lo();
3292+
let span_value = if is_integral && !end_is_negative { "0" } else { "/* start */" };
3293+
3294+
err.span_suggestion_verbose(
3295+
insert_span,
3296+
format!(
3297+
"consider using a bounded `{range_type}` by adding a concrete starting value"
3298+
),
3299+
span_value,
3300+
Applicability::HasPlaceholders,
3301+
);
3302+
}
3303+
}
3304+
}
3305+
32313306
/// Print out the type for use in value namespace.
32323307
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
32333308
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)