Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

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`.
Loading