fix #102396, suggest parentheses for possible range methods
This commit is contained in:
parent
d9f8b4b985
commit
5dd44d4d4c
5 changed files with 339 additions and 15 deletions
|
@ -133,3 +133,8 @@ 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_add_missing_parentheses_in_range = you must surround the range in parentheses to call the `{$func_name}` function
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
//! found or is otherwise invalid.
|
||||
|
||||
use crate::check::FnCtxt;
|
||||
use crate::errors;
|
||||
use rustc_ast::ast::Mutability;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::{
|
||||
|
@ -12,7 +13,7 @@ use rustc_hir as hir;
|
|||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{ExprKind, Node, QPath};
|
||||
use rustc_hir::{is_range_literal, 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};
|
||||
|
@ -271,9 +272,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
};
|
||||
|
||||
if self.suggest_constraining_numerical_ty(
|
||||
if self.suggest_range_for_iter(tcx, actual, source, span, item_name, &ty_str)
|
||||
|| self.suggest_constraining_numerical_ty(
|
||||
tcx, actual, source, span, item_kind, item_name, &ty_str,
|
||||
) {
|
||||
)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -1201,6 +1204,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
false
|
||||
}
|
||||
|
||||
fn suggest_range_for_iter(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
actual: Ty<'tcx>,
|
||||
source: SelfSource<'tcx>,
|
||||
span: Span,
|
||||
item_name: Ident,
|
||||
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)),
|
||||
// `..=` 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
|
||||
);
|
||||
|
||||
if pick.is_ok() {
|
||||
let range_span = parent_expr.span.with_hi(expr.span.hi());
|
||||
tcx.sess.emit_err(errors::MissingParentheseInRange {
|
||||
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(),
|
||||
}
|
||||
)
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn suggest_constraining_numerical_ty(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
@ -1263,7 +1329,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// If this is a floating point literal that ends with '.',
|
||||
// get rid of it to stop this from becoming a member access.
|
||||
let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
|
||||
|
||||
err.span_suggestion(
|
||||
lit.span,
|
||||
&format!(
|
||||
|
|
|
@ -346,3 +346,28 @@ pub struct ExpectedUsedSymbol {
|
|||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis::missing_parentheses_in_range, code = "E0599")]
|
||||
pub struct MissingParentheseInRange {
|
||||
#[primary_span]
|
||||
#[label(hir_analysis::missing_parentheses_in_range)]
|
||||
pub span: Span,
|
||||
pub ty_str: String,
|
||||
|
||||
#[subdiagnostic]
|
||||
pub add_missing_parentheses: Option<AddMissingParenthesesInRange>,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[multipart_suggestion_verbose(
|
||||
hir_analysis::add_missing_parentheses_in_range,
|
||||
applicability = "maybe-incorrect"
|
||||
)]
|
||||
pub struct AddMissingParenthesesInRange {
|
||||
pub func_name: String,
|
||||
#[suggestion_part(code = "(")]
|
||||
pub left: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
pub right: Span,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,70 @@
|
|||
#![allow(unused)]
|
||||
fn main() {
|
||||
let arr = &[0, 1, 2, 3];
|
||||
for _i in 0..arr.len().rev() { //~ERROR not an iterator
|
||||
for _i in 0..arr.len().rev() {
|
||||
//~^ ERROR not an iterator
|
||||
//~| 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.
|
||||
}
|
||||
|
||||
// Test for #102396
|
||||
for i in 1..11.rev() {
|
||||
//~^ ERROR not an iterator
|
||||
//~| HELP surround the range in parentheses
|
||||
}
|
||||
|
||||
let end: usize = 10;
|
||||
for i in 1..end.rev() {
|
||||
//~^ ERROR not an iterator
|
||||
//~| HELP surround the range in parentheses
|
||||
}
|
||||
|
||||
for i in 1..(end + 1).rev() {
|
||||
//~^ ERROR not an iterator
|
||||
//~| HELP surround the range in parentheses
|
||||
}
|
||||
|
||||
if 1..(end + 1).is_empty() {
|
||||
//~^ ERROR not an iterator
|
||||
//~| 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]
|
||||
//~| HELP surround the range in parentheses
|
||||
}
|
||||
|
||||
let _res: i32 = 3..6.take(2).sum();
|
||||
//~^ ERROR `{integer}` is not an iterator [E0599]
|
||||
//~| ERROR mismatched types [E0308]
|
||||
//~| HELP surround the range in parentheses
|
||||
|
||||
let _sum: i32 = 3..6.sum();
|
||||
//~^ ERROR `{integer}` is not an iterator [E0599]
|
||||
//~| ERROR mismatched types [E0308]
|
||||
//~| HELP surround the range in parentheses
|
||||
|
||||
let a = 1 as usize;
|
||||
let b = 10 as usize;
|
||||
|
||||
for _a in a..=b.rev() {
|
||||
//~^ ERROR not an iterator
|
||||
//~| HELP surround the range in parentheses
|
||||
}
|
||||
|
||||
let _res = ..10.contains(3);
|
||||
//~^ ERROR not an iterator
|
||||
//~| HELP surround the range in parentheses
|
||||
|
||||
if 1..end.error_method() {
|
||||
//~^ ERROR no method named `error_method`
|
||||
//~| ERROR mismatched types [E0308]
|
||||
// Won't suggest
|
||||
}
|
||||
|
||||
let _res = b.take(1)..a;
|
||||
//~^ ERROR not an iterator
|
||||
}
|
||||
|
|
|
@ -1,13 +1,179 @@
|
|||
error[E0599]: `usize` is not an iterator
|
||||
--> $DIR/issue-90315.rs:3:26
|
||||
--> $DIR/issue-90315.rs:4:28
|
||||
|
|
||||
LL | for _i in 0..arr.len().rev() {
|
||||
| ^^^ `usize` is not an iterator
|
||||
|
|
||||
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
|
||||
--> $DIR/issue-90315.rs:12:20
|
||||
|
|
||||
LL | for i in 1..11.rev() {
|
||||
| ^^^ `{integer}` is not an iterator
|
||||
|
|
||||
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
|
||||
--> $DIR/issue-90315.rs:18:21
|
||||
|
|
||||
LL | for i in 1..end.rev() {
|
||||
| ^^^ `usize` is not an iterator
|
||||
|
|
||||
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
|
||||
--> $DIR/issue-90315.rs:23:27
|
||||
|
|
||||
LL | for i in 1..(end + 1).rev() {
|
||||
| ^^^ `usize` is not an iterator
|
||||
|
|
||||
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
|
||||
--> $DIR/issue-90315.rs:28:21
|
||||
|
|
||||
LL | if 1..(end + 1).is_empty() {
|
||||
| ^^^^^^^^ `usize` is not an iterator
|
||||
|
|
||||
help: you must surround the range in parentheses to call the `is_empty` function
|
||||
|
|
||||
LL | if (1..(end + 1)).is_empty() {
|
||||
| + +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-90315.rs:28:8
|
||||
|
|
||||
LL | if 1..(end + 1).is_empty() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
|
||||
|
|
||||
= note: expected type `bool`
|
||||
found struct `std::ops::Range<{integer}>`
|
||||
|
||||
error[E0599]: `usize` is not an iterator
|
||||
--> $DIR/issue-90315.rs:34:21
|
||||
|
|
||||
LL | if 1..(end + 1).is_sorted() {
|
||||
| ^^^^^^^^^ `usize` is not an iterator
|
||||
|
|
||||
help: you must surround the range in parentheses to call the `is_sorted` function
|
||||
|
|
||||
LL | if (1..(end + 1)).is_sorted() {
|
||||
| + +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-90315.rs:34:8
|
||||
|
|
||||
LL | if 1..(end + 1).is_sorted() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
|
||||
|
|
||||
= note: expected type `bool`
|
||||
found struct `std::ops::Range<{integer}>`
|
||||
|
||||
error[E0599]: `{integer}` is not an iterator
|
||||
--> $DIR/issue-90315.rs:40:26
|
||||
|
|
||||
LL | let _res: i32 = 3..6.take(2).sum();
|
||||
| ^^^^ `{integer}` is not an iterator
|
||||
|
|
||||
help: you must surround the range in parentheses to call the `take` function
|
||||
|
|
||||
LL | let _res: i32 = (3..6).take(2).sum();
|
||||
| + +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-90315.rs:40:21
|
||||
|
|
||||
LL | let _res: i32 = 3..6.take(2).sum();
|
||||
| --- ^^^^^^^^^^^^^^^^^^ expected `i32`, found struct `std::ops::Range`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found struct `std::ops::Range<{integer}>`
|
||||
|
||||
error[E0599]: `{integer}` is not an iterator
|
||||
--> $DIR/issue-90315.rs:45:26
|
||||
|
|
||||
LL | let _sum: i32 = 3..6.sum();
|
||||
| ^^^ `{integer}` is not an iterator
|
||||
|
|
||||
help: you must surround the range in parentheses to call the `sum` function
|
||||
|
|
||||
LL | let _sum: i32 = (3..6).sum();
|
||||
| + +
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-90315.rs:45:21
|
||||
|
|
||||
LL | let _sum: i32 = 3..6.sum();
|
||||
| --- ^^^^^^^^^^ expected `i32`, found struct `std::ops::Range`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected type `i32`
|
||||
found struct `std::ops::Range<{integer}>`
|
||||
|
||||
error[E0599]: `usize` is not an iterator
|
||||
--> $DIR/issue-90315.rs:53:21
|
||||
|
|
||||
LL | for _a in a..=b.rev() {
|
||||
| ^^^ `usize` is not an iterator
|
||||
|
|
||||
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
|
||||
--> $DIR/issue-90315.rs:58:21
|
||||
|
|
||||
LL | let _res = ..10.contains(3);
|
||||
| ^^^^^^^^ `{integer}` is not an iterator
|
||||
|
|
||||
help: you must surround the range in parentheses to call the `contains` function
|
||||
|
|
||||
LL | let _res = (..10).contains(3);
|
||||
| + +
|
||||
|
||||
error[E0599]: no method named `error_method` found for type `usize` in the current scope
|
||||
--> $DIR/issue-90315.rs:62:15
|
||||
|
|
||||
LL | if 1..end.error_method() {
|
||||
| ^^^^^^^^^^^^ method not found in `usize`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-90315.rs:62:8
|
||||
|
|
||||
LL | if 1..end.error_method() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range`
|
||||
|
|
||||
= note: expected type `bool`
|
||||
found struct `std::ops::Range<{integer}>`
|
||||
|
||||
error[E0599]: `usize` is not an iterator
|
||||
--> $DIR/issue-90315.rs:68:18
|
||||
|
|
||||
LL | let _res = b.take(1)..a;
|
||||
| ^^^^ `usize` is not an iterator
|
||||
|
|
||||
= note: the following trait bounds were not satisfied:
|
||||
`usize: Iterator`
|
||||
which is required by `&mut usize: Iterator`
|
||||
|
||||
error: aborting due to previous error
|
||||
error: aborting due to 17 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
||||
Some errors have detailed explanations: E0308, E0599.
|
||||
For more information about an error, try `rustc --explain E0308`.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue