Rollup merge of #102454 - chenyukang:fix-102396-missing-parentheses, r=lcnr

Suggest parentheses for possible range method calling

Fixes #102396
This commit is contained in:
Matthias Krüger 2022-10-17 17:15:49 +02:00 committed by GitHub
commit 66de34b035
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 385 additions and 13 deletions

View File

@ -133,3 +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_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 its `{$func_name}` function

View File

@ -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::{
@ -271,7 +272,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};
if self.suggest_constraining_numerical_ty(
if self.suggest_wrapping_range_with_parens(
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;
@ -1202,6 +1205,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
false
}
/// Suggest possible range with adding parentheses, for example:
/// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`.
fn suggest_wrapping_range_with_parens(
&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 {
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(...)`.
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.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.probe_for_name(
span,
Mode::MethodCall,
item_name,
IsSuggestion(true),
range_ty,
expr.hir_id,
ProbeScope::AllTraits,
);
if pick.is_ok() {
let range_span = parent_expr.span.with_hi(expr.span.hi());
tcx.sess.emit_err(errors::MissingParentheseInRange {
span,
ty_str: ty_str.to_string(),
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;
}
}
}
}
false
}
fn suggest_constraining_numerical_ty(
&self,
tcx: TyCtxt<'tcx>,
@ -1264,7 +1350,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!(

View File

@ -346,3 +346,29 @@ pub struct ExpectedUsedSymbol {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[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<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,
}

View File

@ -1,7 +1,76 @@
#![allow(unused)]
fn main() {
let arr = &[0,1,2,3];
for _i in 0..arr.len().rev() { //~ERROR not an iterator
let arr = &[0, 1, 2, 3];
for _i in 0..arr.len().rev() {
//~^ 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.
}
// Test for #102396
for i in 1..11.rev() {
//~^ ERROR can't call method
//~| HELP surround the range in parentheses
}
let end: usize = 10;
for i in 1..end.rev() {
//~^ ERROR can't call method
//~| HELP surround the range in parentheses
}
for i in 1..(end + 1).rev() {
//~^ ERROR can't call method
//~| HELP surround the range in parentheses
}
if 1..(end + 1).is_empty() {
//~^ 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 can't call method
//~| HELP surround the range in parentheses
}
let _res: i32 = 3..6.take(2).sum();
//~^ ERROR can't call method
//~| ERROR mismatched types [E0308]
//~| HELP surround the range in parentheses
let _sum: i32 = 3..6.sum();
//~^ ERROR can't call method
//~| 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 can't call method
//~| HELP surround the range in parentheses
}
let _res = ..10.contains(3);
//~^ ERROR can't call method
//~| 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 `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`
}

View File

@ -1,13 +1,201 @@
error[E0599]: `usize` is not an iterator
--> $DIR/issue-90315.rs:3:26
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 its `rev` function
|
LL | for _i in (0..arr.len()).rev() {
| + +
error[E0689]: can't call method `rev` on type `{integer}`
--> $DIR/issue-90315.rs:12:20
|
LL | for i in 1..11.rev() {
| ^^^ can't call method `rev` on type `{integer}`
|
help: you must surround the range in parentheses to call its `rev` function
|
LL | for i in (1..11).rev() {
| + +
error[E0689]: can't call method `rev` on type `usize`
--> $DIR/issue-90315.rs:18:21
|
LL | for i in 1..end.rev() {
| ^^^ can't call method `rev` on type `usize`
|
help: you must surround the range in parentheses to call its `rev` function
|
LL | for i in (1..end).rev() {
| + +
error[E0689]: can't call method `rev` on type `usize`
--> $DIR/issue-90315.rs:23:27
|
LL | for i in 1..(end + 1).rev() {
| ^^^ can't call method `rev` on type `usize`
|
help: you must surround the range in parentheses to call its `rev` function
|
LL | for i in (1..(end + 1)).rev() {
| + +
error[E0689]: can't call method `is_empty` on type `usize`
--> $DIR/issue-90315.rs:28:21
|
LL | if 1..(end + 1).is_empty() {
| ^^^^^^^^ can't call method `is_empty` on type `usize`
|
help: you must surround the range in parentheses to call its `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[E0689]: can't call method `is_sorted` on type `usize`
--> $DIR/issue-90315.rs:34:21
|
LL | if 1..(end + 1).is_sorted() {
| ^^^^^^^^^ can't call method `is_sorted` on type `usize`
|
help: you must surround the range in parentheses to call its `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[E0689]: can't call method `take` on type `{integer}`
--> $DIR/issue-90315.rs:40:26
|
LL | let _res: i32 = 3..6.take(2).sum();
| ^^^^ can't call method `take` on type `{integer}`
|
help: you must surround the range in parentheses to call its `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[E0689]: can't call method `sum` on type `{integer}`
--> $DIR/issue-90315.rs:45:26
|
LL | let _sum: i32 = 3..6.sum();
| ^^^ can't call method `sum` on type `{integer}`
|
help: you must surround the range in parentheses to call its `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[E0689]: can't call method `rev` on type `usize`
--> $DIR/issue-90315.rs:53:21
|
LL | for _a in a..=b.rev() {
| ^^^ can't call method `rev` on type `usize`
|
help: you must surround the range in parentheses to call its `rev` function
|
LL | for _a in (a..=b).rev() {
| + +
error[E0689]: can't call method `contains` on type `{integer}`
--> $DIR/issue-90315.rs:58:21
|
LL | let _res = ..10.contains(3);
| ^^^^^^^^ can't call method `contains` on type `{integer}`
|
help: you must surround the range in parentheses to call its `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[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();
| ~~~~~
For more information about this error, try `rustc --explain 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`.