Skip to content

Commit 66de34b

Browse files
authored
Rollup merge of #102454 - chenyukang:fix-102396-missing-parentheses, r=lcnr
Suggest parentheses for possible range method calling Fixes #102396
2 parents abd7744 + 151001c commit 66de34b

File tree

5 files changed

+384
-12
lines changed

5 files changed

+384
-12
lines changed

compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,7 @@ hir_analysis_extern_crate_not_idiomatic =
133133
.suggestion = convert it to a `{$msg_code}`
134134
135135
hir_analysis_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)`
136+
137+
hir_analysis_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}`
138+
139+
hir_analysis_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function

compiler/rustc_hir_analysis/src/check/method/suggest.rs

+87-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//! found or is otherwise invalid.
33
44
use crate::check::FnCtxt;
5+
use crate::errors;
56
use rustc_ast::ast::Mutability;
67
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
78
use rustc_errors::{
@@ -271,7 +272,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
271272
}
272273
};
273274

274-
if self.suggest_constraining_numerical_ty(
275+
if self.suggest_wrapping_range_with_parens(
276+
tcx, actual, source, span, item_name, &ty_str,
277+
) || self.suggest_constraining_numerical_ty(
275278
tcx, actual, source, span, item_kind, item_name, &ty_str,
276279
) {
277280
return None;
@@ -1202,6 +1205,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12021205
false
12031206
}
12041207

1208+
/// Suggest possible range with adding parentheses, for example:
1209+
/// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`.
1210+
fn suggest_wrapping_range_with_parens(
1211+
&self,
1212+
tcx: TyCtxt<'tcx>,
1213+
actual: Ty<'tcx>,
1214+
source: SelfSource<'tcx>,
1215+
span: Span,
1216+
item_name: Ident,
1217+
ty_str: &str,
1218+
) -> bool {
1219+
if let SelfSource::MethodCall(expr) = source {
1220+
for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) {
1221+
if let Node::Expr(parent_expr) = parent {
1222+
let lang_item = match parent_expr.kind {
1223+
ExprKind::Struct(ref qpath, _, _) => match **qpath {
1224+
QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range),
1225+
QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo),
1226+
QPath::LangItem(LangItem::RangeToInclusive, ..) => {
1227+
Some(LangItem::RangeToInclusive)
1228+
}
1229+
_ => None,
1230+
},
1231+
ExprKind::Call(ref func, _) => match func.kind {
1232+
// `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
1233+
ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => {
1234+
Some(LangItem::RangeInclusiveStruct)
1235+
}
1236+
_ => None,
1237+
},
1238+
_ => None,
1239+
};
1240+
1241+
if lang_item.is_none() {
1242+
continue;
1243+
}
1244+
1245+
let span_included = match parent_expr.kind {
1246+
hir::ExprKind::Struct(_, eps, _) => {
1247+
eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span))
1248+
}
1249+
// `..=` desugars into `::std::ops::RangeInclusive::new(...)`.
1250+
hir::ExprKind::Call(ref func, ..) => func.span.contains(span),
1251+
_ => false,
1252+
};
1253+
1254+
if !span_included {
1255+
continue;
1256+
}
1257+
1258+
let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None);
1259+
let range_ty =
1260+
self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]);
1261+
1262+
let pick = self.probe_for_name(
1263+
span,
1264+
Mode::MethodCall,
1265+
item_name,
1266+
IsSuggestion(true),
1267+
range_ty,
1268+
expr.hir_id,
1269+
ProbeScope::AllTraits,
1270+
);
1271+
if pick.is_ok() {
1272+
let range_span = parent_expr.span.with_hi(expr.span.hi());
1273+
tcx.sess.emit_err(errors::MissingParentheseInRange {
1274+
span,
1275+
ty_str: ty_str.to_string(),
1276+
method_name: item_name.as_str().to_string(),
1277+
add_missing_parentheses: Some(errors::AddMissingParenthesesInRange {
1278+
func_name: item_name.name.as_str().to_string(),
1279+
left: range_span.shrink_to_lo(),
1280+
right: range_span.shrink_to_hi(),
1281+
}),
1282+
});
1283+
return true;
1284+
}
1285+
}
1286+
}
1287+
}
1288+
false
1289+
}
1290+
12051291
fn suggest_constraining_numerical_ty(
12061292
&self,
12071293
tcx: TyCtxt<'tcx>,
@@ -1264,7 +1350,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12641350
// If this is a floating point literal that ends with '.',
12651351
// get rid of it to stop this from becoming a member access.
12661352
let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
1267-
12681353
err.span_suggestion(
12691354
lit.span,
12701355
&format!(

compiler/rustc_hir_analysis/src/errors.rs

+26
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,29 @@ pub struct ExpectedUsedSymbol {
346346
#[primary_span]
347347
pub span: Span,
348348
}
349+
350+
#[derive(Diagnostic)]
351+
#[diag(hir_analysis::missing_parentheses_in_range, code = "E0689")]
352+
pub struct MissingParentheseInRange {
353+
#[primary_span]
354+
#[label(hir_analysis::missing_parentheses_in_range)]
355+
pub span: Span,
356+
pub ty_str: String,
357+
pub method_name: String,
358+
359+
#[subdiagnostic]
360+
pub add_missing_parentheses: Option<AddMissingParenthesesInRange>,
361+
}
362+
363+
#[derive(Subdiagnostic)]
364+
#[multipart_suggestion_verbose(
365+
hir_analysis::add_missing_parentheses_in_range,
366+
applicability = "maybe-incorrect"
367+
)]
368+
pub struct AddMissingParenthesesInRange {
369+
pub func_name: String,
370+
#[suggestion_part(code = "(")]
371+
pub left: Span,
372+
#[suggestion_part(code = ")")]
373+
pub right: Span,
374+
}
+74-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,76 @@
1+
#![allow(unused)]
12
fn main() {
2-
let arr = &[0,1,2,3];
3-
for _i in 0..arr.len().rev() { //~ERROR not an iterator
4-
// The above error used to say “the method `rev` exists for type `usize`”.
5-
// This regression test ensures it doesn't say that any more.
6-
}
3+
let arr = &[0, 1, 2, 3];
4+
for _i in 0..arr.len().rev() {
5+
//~^ ERROR can't call method
6+
//~| surround the range in parentheses
7+
// The above error used to say “the method `rev` exists for type `usize`”.
8+
// This regression test ensures it doesn't say that any more.
9+
}
10+
11+
// Test for #102396
12+
for i in 1..11.rev() {
13+
//~^ ERROR can't call method
14+
//~| HELP surround the range in parentheses
15+
}
16+
17+
let end: usize = 10;
18+
for i in 1..end.rev() {
19+
//~^ ERROR can't call method
20+
//~| HELP surround the range in parentheses
21+
}
22+
23+
for i in 1..(end + 1).rev() {
24+
//~^ ERROR can't call method
25+
//~| HELP surround the range in parentheses
26+
}
27+
28+
if 1..(end + 1).is_empty() {
29+
//~^ ERROR can't call method
30+
//~| ERROR mismatched types [E0308]
31+
//~| HELP surround the range in parentheses
32+
}
33+
34+
if 1..(end + 1).is_sorted() {
35+
//~^ ERROR mismatched types [E0308]
36+
//~| ERROR can't call method
37+
//~| HELP surround the range in parentheses
38+
}
39+
40+
let _res: i32 = 3..6.take(2).sum();
41+
//~^ ERROR can't call method
42+
//~| ERROR mismatched types [E0308]
43+
//~| HELP surround the range in parentheses
44+
45+
let _sum: i32 = 3..6.sum();
46+
//~^ ERROR can't call method
47+
//~| ERROR mismatched types [E0308]
48+
//~| HELP surround the range in parentheses
49+
50+
let a = 1 as usize;
51+
let b = 10 as usize;
52+
53+
for _a in a..=b.rev() {
54+
//~^ ERROR can't call method
55+
//~| HELP surround the range in parentheses
56+
}
57+
58+
let _res = ..10.contains(3);
59+
//~^ ERROR can't call method
60+
//~| HELP surround the range in parentheses
61+
62+
if 1..end.error_method() {
63+
//~^ ERROR no method named `error_method`
64+
//~| ERROR mismatched types [E0308]
65+
// Won't suggest
66+
}
67+
68+
let _res = b.take(1)..a;
69+
//~^ ERROR `usize` is not an iterator
70+
71+
let _res: i32 = ..6.take(2).sum();
72+
//~^ can't call method `take` on ambiguous numeric type
73+
//~| ERROR mismatched types [E0308]
74+
//~| HELP you must specify a concrete type for this numeric value
75+
// Won't suggest because `RangeTo` dest not implemented `take`
776
}

0 commit comments

Comments
 (0)