diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl index 827c3c93a73..5a7aee9c1c1 100644 --- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl +++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl @@ -133,8 +133,7 @@ hir_analysis_extern_crate_not_idiomatic = .suggestion = convert it to a `{$msg_code}` hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` -hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` -hir_analysis_missing_parentheses_in_range = `{$ty_str}` is not an iterator +hir_analysis_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}` hir_analysis_add_missing_parentheses_in_range = you must surround the range in parentheses to call the `{$func_name}` function diff --git a/compiler/rustc_hir_analysis/src/check/method/suggest.rs b/compiler/rustc_hir_analysis/src/check/method/suggest.rs index d7189e70d06..9aa1b2e2c0a 100644 --- a/compiler/rustc_hir_analysis/src/check/method/suggest.rs +++ b/compiler/rustc_hir_analysis/src/check/method/suggest.rs @@ -13,7 +13,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; -use rustc_hir::{is_range_literal, ExprKind, Node, QPath}; +use rustc_hir::{ExprKind, Node, QPath}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; @@ -1214,50 +1214,62 @@ fn suggest_range_for_iter( ty_str: &str, ) -> bool { if let SelfSource::MethodCall(expr) = source { - let mut search_limit = 5; - for (_, parent) in tcx.hir().parent_iter(expr.hir_id) { - search_limit -= 1; - if search_limit == 0 { - break; - } - - if let Node::Expr(parent_expr) = parent && is_range_literal(parent_expr) { - let span_included = match parent_expr.kind { - hir::ExprKind::Struct(_, eps, _) => - eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)), + for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) { + if let Node::Expr(parent_expr) = parent { + let lang_item = match parent_expr.kind { + ExprKind::Struct(ref qpath, _, _) => match **qpath { + QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range), + QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo), + QPath::LangItem(LangItem::RangeToInclusive, ..) => { + Some(LangItem::RangeToInclusive) + } + _ => None, + }, + ExprKind::Call(ref func, _) => match func.kind { // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. - hir::ExprKind::Call(ref func, ..) => func.span.contains(span), - _ => false, + ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => { + Some(LangItem::RangeInclusiveStruct) + } + _ => None, + }, + _ => None, + }; + + if lang_item.is_none() { + continue; + } + + let span_included = match parent_expr.kind { + hir::ExprKind::Struct(_, eps, _) => { + eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)) + } + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + hir::ExprKind::Call(ref func, ..) => func.span.contains(span), + _ => false, }; if !span_included { continue; } - let range_def_id = self.tcx.lang_items().range_struct().unwrap(); - let range_ty = self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); - - // avoid suggesting when the method name is not implemented for a `range` - let pick = self.lookup_probe( - span, - item_name, - range_ty, - expr, - ProbeScope::AllTraits - ); + debug!("lang_item: {:?}", lang_item); + let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None); + let range_ty = + self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); + let pick = + self.lookup_probe(span, item_name, range_ty, expr, ProbeScope::AllTraits); if pick.is_ok() { let range_span = parent_expr.span.with_hi(expr.span.hi()); tcx.sess.emit_err(errors::MissingParentheseInRange { - span: span, + span, ty_str: ty_str.to_string(), - add_missing_parentheses: Some( - errors::AddMissingParenthesesInRange { - func_name: item_name.name.as_str().to_string(), - left: range_span.shrink_to_lo(), - right: range_span.shrink_to_hi(), - } - ) + method_name: item_name.as_str().to_string(), + add_missing_parentheses: Some(errors::AddMissingParenthesesInRange { + func_name: item_name.name.as_str().to_string(), + left: range_span.shrink_to_lo(), + right: range_span.shrink_to_hi(), + }), }); return true; } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 6634444c636..41f73323d9a 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -348,12 +348,13 @@ pub struct ExpectedUsedSymbol { } #[derive(Diagnostic)] -#[diag(hir_analysis::missing_parentheses_in_range, code = "E0599")] +#[diag(hir_analysis::missing_parentheses_in_range, code = "E0689")] pub struct MissingParentheseInRange { #[primary_span] #[label(hir_analysis::missing_parentheses_in_range)] pub span: Span, pub ty_str: String, + pub method_name: String, #[subdiagnostic] pub add_missing_parentheses: Option, diff --git a/src/test/ui/methods/issues/issue-90315.rs b/src/test/ui/methods/issues/issue-90315.rs index 74cd2b35834..79cdc41959a 100644 --- a/src/test/ui/methods/issues/issue-90315.rs +++ b/src/test/ui/methods/issues/issue-90315.rs @@ -2,7 +2,7 @@ fn main() { let arr = &[0, 1, 2, 3]; for _i in 0..arr.len().rev() { - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| surround the range in parentheses // The above error used to say “the method `rev` exists for type `usize`”. // This regression test ensures it doesn't say that any more. @@ -10,40 +10,40 @@ fn main() { // Test for #102396 for i in 1..11.rev() { - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| HELP surround the range in parentheses } let end: usize = 10; for i in 1..end.rev() { - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| HELP surround the range in parentheses } for i in 1..(end + 1).rev() { - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| HELP surround the range in parentheses } if 1..(end + 1).is_empty() { - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| ERROR mismatched types [E0308] //~| HELP surround the range in parentheses } if 1..(end + 1).is_sorted() { //~^ ERROR mismatched types [E0308] - //~| ERROR `usize` is not an iterator [E0599] + //~| ERROR can't call method //~| HELP surround the range in parentheses } let _res: i32 = 3..6.take(2).sum(); - //~^ ERROR `{integer}` is not an iterator [E0599] + //~^ ERROR can't call method //~| ERROR mismatched types [E0308] //~| HELP surround the range in parentheses let _sum: i32 = 3..6.sum(); - //~^ ERROR `{integer}` is not an iterator [E0599] + //~^ ERROR can't call method //~| ERROR mismatched types [E0308] //~| HELP surround the range in parentheses @@ -51,12 +51,12 @@ fn main() { let b = 10 as usize; for _a in a..=b.rev() { - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| HELP surround the range in parentheses } let _res = ..10.contains(3); - //~^ ERROR not an iterator + //~^ ERROR can't call method //~| HELP surround the range in parentheses if 1..end.error_method() { @@ -66,5 +66,11 @@ fn main() { } let _res = b.take(1)..a; - //~^ ERROR not an iterator + //~^ ERROR `usize` is not an iterator + + let _res: i32 = ..6.take(2).sum(); + //~^ can't call method `take` on ambiguous numeric type + //~| ERROR mismatched types [E0308] + //~| HELP you must specify a concrete type for this numeric value + // Won't suggest because `RangeTo` dest not implemented `take` } diff --git a/src/test/ui/methods/issues/issue-90315.stderr b/src/test/ui/methods/issues/issue-90315.stderr index f2084b593c2..581d6fb4fc9 100644 --- a/src/test/ui/methods/issues/issue-90315.stderr +++ b/src/test/ui/methods/issues/issue-90315.stderr @@ -1,52 +1,52 @@ -error[E0599]: `usize` is not an iterator +error[E0689]: can't call method `rev` on type `usize` --> $DIR/issue-90315.rs:4:28 | LL | for _i in 0..arr.len().rev() { - | ^^^ `usize` is not an iterator + | ^^^ can't call method `rev` on type `usize` | help: you must surround the range in parentheses to call the `rev` function | LL | for _i in (0..arr.len()).rev() { | + + -error[E0599]: `{integer}` is not an iterator +error[E0689]: can't call method `rev` on type `{integer}` --> $DIR/issue-90315.rs:12:20 | LL | for i in 1..11.rev() { - | ^^^ `{integer}` is not an iterator + | ^^^ can't call method `rev` on type `{integer}` | help: you must surround the range in parentheses to call the `rev` function | LL | for i in (1..11).rev() { | + + -error[E0599]: `usize` is not an iterator +error[E0689]: can't call method `rev` on type `usize` --> $DIR/issue-90315.rs:18:21 | LL | for i in 1..end.rev() { - | ^^^ `usize` is not an iterator + | ^^^ can't call method `rev` on type `usize` | help: you must surround the range in parentheses to call the `rev` function | LL | for i in (1..end).rev() { | + + -error[E0599]: `usize` is not an iterator +error[E0689]: can't call method `rev` on type `usize` --> $DIR/issue-90315.rs:23:27 | LL | for i in 1..(end + 1).rev() { - | ^^^ `usize` is not an iterator + | ^^^ can't call method `rev` on type `usize` | help: you must surround the range in parentheses to call the `rev` function | LL | for i in (1..(end + 1)).rev() { | + + -error[E0599]: `usize` is not an iterator +error[E0689]: can't call method `is_empty` on type `usize` --> $DIR/issue-90315.rs:28:21 | LL | if 1..(end + 1).is_empty() { - | ^^^^^^^^ `usize` is not an iterator + | ^^^^^^^^ can't call method `is_empty` on type `usize` | help: you must surround the range in parentheses to call the `is_empty` function | @@ -62,11 +62,11 @@ LL | if 1..(end + 1).is_empty() { = note: expected type `bool` found struct `std::ops::Range<{integer}>` -error[E0599]: `usize` is not an iterator +error[E0689]: can't call method `is_sorted` on type `usize` --> $DIR/issue-90315.rs:34:21 | LL | if 1..(end + 1).is_sorted() { - | ^^^^^^^^^ `usize` is not an iterator + | ^^^^^^^^^ can't call method `is_sorted` on type `usize` | help: you must surround the range in parentheses to call the `is_sorted` function | @@ -82,11 +82,11 @@ LL | if 1..(end + 1).is_sorted() { = note: expected type `bool` found struct `std::ops::Range<{integer}>` -error[E0599]: `{integer}` is not an iterator +error[E0689]: can't call method `take` on type `{integer}` --> $DIR/issue-90315.rs:40:26 | LL | let _res: i32 = 3..6.take(2).sum(); - | ^^^^ `{integer}` is not an iterator + | ^^^^ can't call method `take` on type `{integer}` | help: you must surround the range in parentheses to call the `take` function | @@ -104,11 +104,11 @@ LL | let _res: i32 = 3..6.take(2).sum(); = note: expected type `i32` found struct `std::ops::Range<{integer}>` -error[E0599]: `{integer}` is not an iterator +error[E0689]: can't call method `sum` on type `{integer}` --> $DIR/issue-90315.rs:45:26 | LL | let _sum: i32 = 3..6.sum(); - | ^^^ `{integer}` is not an iterator + | ^^^ can't call method `sum` on type `{integer}` | help: you must surround the range in parentheses to call the `sum` function | @@ -126,22 +126,22 @@ LL | let _sum: i32 = 3..6.sum(); = note: expected type `i32` found struct `std::ops::Range<{integer}>` -error[E0599]: `usize` is not an iterator +error[E0689]: can't call method `rev` on type `usize` --> $DIR/issue-90315.rs:53:21 | LL | for _a in a..=b.rev() { - | ^^^ `usize` is not an iterator + | ^^^ can't call method `rev` on type `usize` | help: you must surround the range in parentheses to call the `rev` function | LL | for _a in (a..=b).rev() { | + + -error[E0599]: `{integer}` is not an iterator +error[E0689]: can't call method `contains` on type `{integer}` --> $DIR/issue-90315.rs:58:21 | LL | let _res = ..10.contains(3); - | ^^^^^^^^ `{integer}` is not an iterator + | ^^^^^^^^ can't call method `contains` on type `{integer}` | help: you must surround the range in parentheses to call the `contains` function | @@ -173,7 +173,29 @@ LL | let _res = b.take(1)..a; `usize: Iterator` which is required by `&mut usize: Iterator` -error: aborting due to 17 previous errors +error[E0689]: can't call method `take` on ambiguous numeric type `{integer}` + --> $DIR/issue-90315.rs:71:25 + | +LL | let _res: i32 = ..6.take(2).sum(); + | ^^^^ + | +help: you must specify a concrete type for this numeric value, like `i32` + | +LL | let _res: i32 = ..6_i32.take(2).sum(); + | ~~~~~ -Some errors have detailed explanations: E0308, E0599. +error[E0308]: mismatched types + --> $DIR/issue-90315.rs:71:21 + | +LL | let _res: i32 = ..6.take(2).sum(); + | --- ^^^^^^^^^^^^^^^^^ expected `i32`, found struct `RangeTo` + | | + | expected due to this + | + = note: expected type `i32` + found struct `RangeTo<_>` + +error: aborting due to 19 previous errors + +Some errors have detailed explanations: E0308, E0599, E0689. For more information about an error, try `rustc --explain E0308`.