Skip to content

Commit 0c0685b

Browse files
committed
review comments: make suggestion more accurate
1 parent c9381fc commit 0c0685b

File tree

5 files changed

+73
-31
lines changed

5 files changed

+73
-31
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1604,7 +1604,6 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16041604
None,
16051605
Some(coercion_error),
16061606
);
1607-
fcx.check_for_range_as_method_call(&mut err, expr, found, expected);
16081607
}
16091608

16101609
if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {

compiler/rustc_hir_typeck/src/demand.rs

+32-8
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7171
self.note_type_is_not_clone(err, expected, expr_ty, expr);
7272
self.note_need_for_fn_pointer(err, expected, expr_ty);
7373
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
74+
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
7475
}
7576

7677
/// Requires that the two types unify, and prints an error message if
@@ -1449,14 +1450,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14491450
}
14501451
}
14511452

1453+
/// Identify when the user has written `foo..bar()` instead of `foo.bar()`.
14521454
pub fn check_for_range_as_method_call(
14531455
&self,
14541456
err: &mut Diagnostic,
14551457
expr: &hir::Expr<'_>,
14561458
checked_ty: Ty<'tcx>,
1457-
// FIXME: We should do analysis to see if we can synthesize an expresion that produces
1458-
// this type for always accurate suggestions, or at least marking the suggestion as
1459-
// machine applicable.
14601459
expected_ty: Ty<'tcx>,
14611460
) {
14621461
if !hir::is_range_literal(expr) {
@@ -1467,13 +1466,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14671466
[start, end],
14681467
_,
14691468
) = expr.kind else { return; };
1469+
let parent = self.tcx.hir().get_parent_node(expr.hir_id);
1470+
if let Some(hir::Node::ExprField(_)) = self.tcx.hir().find(parent) {
1471+
// Ignore `Foo { field: a..Default::default() }`
1472+
return;
1473+
}
14701474
let mut expr = end.expr;
14711475
while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
14721476
// Getting to the root receiver and asserting it is a fn call let's us ignore cases in
14731477
// `src/test/ui/methods/issues/issue-90315.stderr`.
14741478
expr = rcvr;
14751479
}
1476-
let hir::ExprKind::Call(..) = expr.kind else { return; };
1480+
let hir::ExprKind::Call(method_name, _) = expr.kind else { return; };
14771481
let ty::Adt(adt, _) = checked_ty.kind() else { return; };
14781482
if self.tcx.lang_items().range_struct() != Some(adt.did()) {
14791483
return;
@@ -1483,11 +1487,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14831487
{
14841488
return;
14851489
}
1490+
// Check if start has method named end.
1491+
let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else { return; };
1492+
let [hir::PathSegment { ident, .. }] = p.segments else { return; };
1493+
let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
1494+
let Ok(_pick) = self.probe_for_name(
1495+
probe::Mode::MethodCall,
1496+
*ident,
1497+
probe::IsSuggestion(true),
1498+
self_ty,
1499+
expr.hir_id,
1500+
probe::ProbeScope::AllTraits,
1501+
) else { return; };
1502+
let mut sugg = ".";
1503+
let mut span = start.expr.span.between(end.expr.span);
1504+
if span.lo() + BytePos(2) == span.hi() {
1505+
// There's no space between the start, the range op and the end, suggest removal which
1506+
// will be more noticeable than the replacement of `..` with `.`.
1507+
span = span.with_lo(span.lo() + BytePos(1));
1508+
sugg = "";
1509+
}
14861510
err.span_suggestion_verbose(
1487-
start.expr.span.between(end.expr.span),
1488-
"you might have meant to write a method call instead of a range",
1489-
".".to_string(),
1490-
Applicability::MaybeIncorrect,
1511+
span,
1512+
"you likely meant to write a method call instead of a range",
1513+
sugg,
1514+
Applicability::MachineApplicable,
14911515
);
14921516
}
14931517
}

compiler/rustc_resolve/src/late.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -3344,10 +3344,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
33443344
let suggestion = if let Some((start, end)) = this.diagnostic_metadata.in_range
33453345
&& path[0].ident.span.lo() == end.span.lo()
33463346
{
3347+
let mut sugg = ".";
3348+
let mut span = start.span.between(end.span);
3349+
if span.lo() + BytePos(2) == span.hi() {
3350+
// There's no space between the start, the range op and the end, suggest
3351+
// removal which will look better.
3352+
span = span.with_lo(span.lo() + BytePos(1));
3353+
sugg = "";
3354+
}
33473355
Some((
3348-
start.span.between(end.span),
3356+
span,
33493357
"you might have meant to write a method call instead of a range",
3350-
".".to_string(),
3358+
sugg.to_string(),
33513359
Applicability::MaybeIncorrect,
33523360
))
33533361
} else if res.is_none() {

src/test/ui/suggestions/method-access-to-range-literal-typo.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,22 @@ fn as_ref() -> Option<Vec<u8>> {
44
struct Type {
55
option: Option<Vec<u8>>
66
}
7+
trait Trait {
8+
fn foo(&self) -> Vec<u8>;
9+
}
10+
impl Trait for Option<Vec<u8>> {
11+
fn foo(&self) -> Vec<u8> {
12+
vec![1, 2, 3]
13+
}
14+
}
715

816
impl Type {
917
fn method(&self) -> Option<Vec<u8>> {
1018
self.option..as_ref().map(|x| x)
1119
//~^ ERROR E0308
1220
}
13-
fn method2(&self) -> Option<Vec<u8>> {
14-
self.option..foo().map(|x| x)
21+
fn method2(&self) -> &u8 {
22+
self.option..foo().get(0)
1523
//~^ ERROR E0425
1624
//~| ERROR E0308
1725
}

src/test/ui/suggestions/method-access-to-range-literal-typo.stderr

+21-18
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
error[E0425]: cannot find function `foo` in this scope
2-
--> $DIR/method-access-to-range-literal-typo.rs:14:22
2+
--> $DIR/method-access-to-range-literal-typo.rs:22:22
33
|
4-
LL | self.option..foo().map(|x| x)
4+
LL | self.option..foo().get(0)
55
| ^^^ not found in this scope
66
|
77
help: you might have meant to write a method call instead of a range
88
|
9-
LL | self.option.foo().map(|x| x)
10-
| ~
9+
LL - self.option..foo().get(0)
10+
LL + self.option.foo().get(0)
11+
|
1112

1213
error[E0308]: mismatched types
13-
--> $DIR/method-access-to-range-literal-typo.rs:10:9
14+
--> $DIR/method-access-to-range-literal-typo.rs:18:9
1415
|
1516
LL | fn method(&self) -> Option<Vec<u8>> {
1617
| --------------- expected `Option<Vec<u8>>` because of return type
@@ -19,25 +20,27 @@ LL | self.option..as_ref().map(|x| x)
1920
|
2021
= note: expected enum `Option<_>`
2122
found struct `std::ops::Range<Option<_>>`
22-
help: you might have meant to write a method call instead of a range
23+
help: you likely meant to write a method call instead of a range
24+
|
25+
LL - self.option..as_ref().map(|x| x)
26+
LL + self.option.as_ref().map(|x| x)
2327
|
24-
LL | self.option.as_ref().map(|x| x)
25-
| ~
2628

2729
error[E0308]: mismatched types
28-
--> $DIR/method-access-to-range-literal-typo.rs:14:9
30+
--> $DIR/method-access-to-range-literal-typo.rs:22:9
2931
|
30-
LL | fn method2(&self) -> Option<Vec<u8>> {
31-
| --------------- expected `Option<Vec<u8>>` because of return type
32-
LL | self.option..foo().map(|x| x)
33-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found struct `Range`
32+
LL | fn method2(&self) -> &u8 {
33+
| --- expected `&u8` because of return type
34+
LL | self.option..foo().get(0)
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found struct `Range`
3436
|
35-
= note: expected enum `Option<_>`
36-
found struct `std::ops::Range<Option<_>>`
37-
help: you might have meant to write a method call instead of a range
37+
= note: expected reference `&u8`
38+
found struct `std::ops::Range<Option<Vec<u8>>>`
39+
help: you likely meant to write a method call instead of a range
40+
|
41+
LL - self.option..foo().get(0)
42+
LL + self.option.foo().get(0)
3843
|
39-
LL | self.option.foo().map(|x| x)
40-
| ~
4144

4245
error: aborting due to 3 previous errors
4346

0 commit comments

Comments
 (0)