Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected);
self.suggest_bounds_for_range_to_method(&mut err, source, item_ident);
err.emit()
}

Expand Down Expand Up @@ -3260,6 +3261,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

fn suggest_bounds_for_range_to_method(
&self,
err: &mut Diag<'_>,
source: SelfSource<'tcx>,
item_ident: Ident,
) {
let SelfSource::MethodCall(rcvr_expr) = source else { return };
let hir::ExprKind::Struct(qpath, fields, _) = rcvr_expr.kind else { return };
let Some(lang_item) = self.tcx.qpath_lang_item(*qpath) else {
return;
};
let is_inclusive = match lang_item {
hir::LangItem::RangeTo => false,
hir::LangItem::RangeToInclusive | hir::LangItem::RangeInclusiveCopy => true,
_ => return,
};

let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) else { return };
let Some(_) = self
.tcx
.associated_items(iterator_trait)
.filter_by_name_unhygienic(item_ident.name)
.next()
else {
return;
};

let source_map = self.tcx.sess.source_map();
let range_type = if is_inclusive { "RangeInclusive" } else { "Range" };
let Some(end_field) = fields.iter().find(|f| f.ident.name == rustc_span::sym::end) else {
return;
};

let element_ty = self.typeck_results.borrow().expr_ty_opt(end_field.expr);
let is_integral = element_ty.is_some_and(|ty| ty.is_integral());
let end_is_negative = is_integral
&& matches!(end_field.expr.kind, hir::ExprKind::Unary(rustc_ast::UnOp::Neg, _));

let Ok(snippet) = source_map.span_to_snippet(rcvr_expr.span) else { return };

let offset = snippet
.chars()
.take_while(|&c| c == '(' || c.is_whitespace())
.map(|c| c.len_utf8())
.sum::<usize>();

let insert_span = rcvr_expr
.span
.with_lo(rcvr_expr.span.lo() + rustc_span::BytePos(offset as u32))
.shrink_to_lo();

let (value, appl) = if is_integral && !end_is_negative {
("0", Applicability::MachineApplicable)
} else {
("/* start */", Applicability::HasPlaceholders)
};

err.span_suggestion_verbose(
insert_span,
format!("consider using a bounded `{range_type}` by adding a concrete starting value"),
value,
appl,
);
}

/// Print out the type for use in value namespace.
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
match ty.kind() {
Expand Down
38 changes: 38 additions & 0 deletions tests/ui/range/range-to-iterator-suggestion-issue-147749.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
fn main() {
for x in (..4).rev() {
//~^ ERROR `RangeTo<{integer}>` is not an iterator
//~| HELP consider using a bounded `Range` by adding a concrete starting value
let _ = x;
}

for x in (..=4).rev() {
//~^ ERROR `std::ops::RangeToInclusive<{integer}>` is not an iterator
//~| HELP consider using a bounded `RangeInclusive` by adding a concrete starting value
let _ = x;
}

// should not suggest for `iter` method
let _v: Vec<_> = (..5).iter().collect();
//~^ ERROR no method named `iter` found

for _x in (..'a').rev() {}
//~^ ERROR `RangeTo<char>` is not an iterator
//~| HELP consider using a bounded `Range` by adding a concrete starting value

for _x in (..='a').rev() {}
//~^ ERROR `std::ops::RangeToInclusive<char>` is not an iterator
//~| HELP consider using a bounded `RangeInclusive` by adding a concrete starting value

for _x in (..-10).rev() {}
//~^ ERROR `RangeTo<{integer}>` is not an iterator
//~| HELP consider using a bounded `Range` by adding a concrete starting value

for _x in (..=-10).rev() {}
//~^ ERROR `std::ops::RangeToInclusive<{integer}>` is not an iterator
//~| HELP consider using a bounded `RangeInclusive` by adding a concrete starting value

let end_val = 10;
for _x in (..-end_val).rev() {}
//~^ ERROR `RangeTo<{integer}>` is not an iterator
//~| HELP consider using a bounded `Range` by adding a concrete starting value
}
114 changes: 114 additions & 0 deletions tests/ui/range/range-to-iterator-suggestion-issue-147749.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
error[E0599]: `RangeTo<{integer}>` is not an iterator
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:2:20
|
LL | for x in (..4).rev() {
| ^^^ `RangeTo<{integer}>` is not an iterator
|
= note: the following trait bounds were not satisfied:
`RangeTo<{integer}>: Iterator`
which is required by `&mut RangeTo<{integer}>: Iterator`
= note: you might have meant to use a bounded `Range`
help: consider using a bounded `Range` by adding a concrete starting value
|
LL | for x in (0..4).rev() {
| +

error[E0599]: `std::ops::RangeToInclusive<{integer}>` is not an iterator
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:8:21
|
LL | for x in (..=4).rev() {
| ^^^ `std::ops::RangeToInclusive<{integer}>` is not an iterator
|
= note: the following trait bounds were not satisfied:
`std::ops::RangeToInclusive<{integer}>: Iterator`
which is required by `&mut std::ops::RangeToInclusive<{integer}>: Iterator`
= note: you might have meant to use a bounded `RangeInclusive`
help: consider using a bounded `RangeInclusive` by adding a concrete starting value
|
LL | for x in (0..=4).rev() {
| +

error[E0599]: no method named `iter` found for struct `RangeTo<Idx>` in the current scope
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:15:28
|
LL | let _v: Vec<_> = (..5).iter().collect();
| ^^^^ method not found in `RangeTo<{integer}>`

error[E0599]: `RangeTo<char>` is not an iterator
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:18:23
|
LL | for _x in (..'a').rev() {}
| ^^^ `RangeTo<char>` is not an iterator
|
= note: the following trait bounds were not satisfied:
`RangeTo<char>: Iterator`
which is required by `&mut RangeTo<char>: Iterator`
= note: you might have meant to use a bounded `Range`
help: consider using a bounded `Range` by adding a concrete starting value
|
LL | for _x in (/* start */..'a').rev() {}
| +++++++++++

error[E0599]: `std::ops::RangeToInclusive<char>` is not an iterator
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:22:24
|
LL | for _x in (..='a').rev() {}
| ^^^ `std::ops::RangeToInclusive<char>` is not an iterator
|
= note: the following trait bounds were not satisfied:
`std::ops::RangeToInclusive<char>: Iterator`
which is required by `&mut std::ops::RangeToInclusive<char>: Iterator`
= note: you might have meant to use a bounded `RangeInclusive`
help: consider using a bounded `RangeInclusive` by adding a concrete starting value
|
LL | for _x in (/* start */..='a').rev() {}
| +++++++++++

error[E0599]: `RangeTo<{integer}>` is not an iterator
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:26:23
|
LL | for _x in (..-10).rev() {}
| ^^^ `RangeTo<{integer}>` is not an iterator
|
= note: the following trait bounds were not satisfied:
`RangeTo<{integer}>: Iterator`
which is required by `&mut RangeTo<{integer}>: Iterator`
= note: you might have meant to use a bounded `Range`
help: consider using a bounded `Range` by adding a concrete starting value
|
LL | for _x in (/* start */..-10).rev() {}
| +++++++++++

error[E0599]: `std::ops::RangeToInclusive<{integer}>` is not an iterator
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:30:24
|
LL | for _x in (..=-10).rev() {}
| ^^^ `std::ops::RangeToInclusive<{integer}>` is not an iterator
|
= note: the following trait bounds were not satisfied:
`std::ops::RangeToInclusive<{integer}>: Iterator`
which is required by `&mut std::ops::RangeToInclusive<{integer}>: Iterator`
= note: you might have meant to use a bounded `RangeInclusive`
help: consider using a bounded `RangeInclusive` by adding a concrete starting value
|
LL | for _x in (/* start */..=-10).rev() {}
| +++++++++++

error[E0599]: `RangeTo<{integer}>` is not an iterator
--> $DIR/range-to-iterator-suggestion-issue-147749.rs:35:28
|
LL | for _x in (..-end_val).rev() {}
| ^^^ `RangeTo<{integer}>` is not an iterator
|
= note: the following trait bounds were not satisfied:
`RangeTo<{integer}>: Iterator`
which is required by `&mut RangeTo<{integer}>: Iterator`
= note: you might have meant to use a bounded `Range`
help: consider using a bounded `Range` by adding a concrete starting value
|
LL | for _x in (/* start */..-end_val).rev() {}
| +++++++++++

error: aborting due to 8 previous errors

For more information about this error, try `rustc --explain E0599`.