Skip to content
Open
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
75 changes: 75 additions & 0 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,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 @@ -3281,6 +3282,80 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

fn suggest_bounds_for_range_to_method(
&self,
Copy link
Member

@fmease fmease Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should only suggest 0 if the element type is integral.

Right now, you suggest (0..'f').rev() for (..'f').rev(). This affects all types that implement the Step trait and aren't integral.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, should it be 0 if the element is signed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it's not good to suggest (0..-10), so I changed the code to only suggest 0 for integral and without negative sign, otherwise suggest a place hold _, not sure this is the best way, but I didn't come out with a better one.

it's complex to handle all scenario, for instance the code maybe (...val), while val is a negative value or some other types..

anyway, this is a MaybeIncorrect suggestion.

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

let tcx = self.tcx;
if let Some(iterator_trait) = tcx.get_diagnostic_item(sym::Iterator) {
let has_method = tcx
.associated_items(iterator_trait)
.filter_by_name_unhygienic(item_ident.name)
.next()
.is_some();

if !has_method {
return;
}
}

let element_ty = self.typeck_results.borrow().expr_ty_opt(rcvr_expr).and_then(|ty| {
if let ty::Adt(_, args) = ty.kind() {
args.get(0).and_then(|arg| arg.as_type())
} else {
None
}
});

let is_integral = element_ty.is_some_and(|ty| ty.is_integral());
let source_map = self.tcx.sess.source_map();
let range_type = if is_inclusive { "RangeInclusive" } else { "Range" };

// Check if the end bound is a negative number
let end_is_negative = is_integral
&& fields.iter().find(|f| f.ident.name == rustc_span::sym::end).is_some_and(
|end_field| {
matches!(end_field.expr.kind, hir::ExprKind::Unary(rustc_ast::UnOp::Neg, _))
},
);

if let Ok(snippet) = source_map.span_to_snippet(rcvr_expr.span) {
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 span_value = if is_integral && !end_is_negative { "0" } else { "/* start */" };

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

/// Print out the type for use in value namespace.
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
match ty.kind() {
Expand Down
33 changes: 33 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,33 @@
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
}
99 changes: 99 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,99 @@
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: aborting due to 7 previous errors

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