None,
Some(coercion_error),
);
- fcx.check_for_range_as_method_call(&mut err, expr, found, expected);
}
if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
self.note_type_is_not_clone(err, expected, expr_ty, expr);
self.note_need_for_fn_pointer(err, expected, expr_ty);
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
+ self.check_for_range_as_method_call(err, expr, expr_ty, expected);
}
/// Requires that the two types unify, and prints an error message if
}
}
+ /// Identify when the user has written `foo..bar()` instead of `foo.bar()`.
pub fn check_for_range_as_method_call(
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
checked_ty: Ty<'tcx>,
- // FIXME: We should do analysis to see if we can synthesize an expresion that produces
- // this type for always accurate suggestions, or at least marking the suggestion as
- // machine applicable.
expected_ty: Ty<'tcx>,
) {
if !hir::is_range_literal(expr) {
[start, end],
_,
) = expr.kind else { return; };
+ let parent = self.tcx.hir().get_parent_node(expr.hir_id);
+ if let Some(hir::Node::ExprField(_)) = self.tcx.hir().find(parent) {
+ // Ignore `Foo { field: a..Default::default() }`
+ return;
+ }
let mut expr = end.expr;
while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
// Getting to the root receiver and asserting it is a fn call let's us ignore cases in
// `src/test/ui/methods/issues/issue-90315.stderr`.
expr = rcvr;
}
- let hir::ExprKind::Call(..) = expr.kind else { return; };
+ let hir::ExprKind::Call(method_name, _) = expr.kind else { return; };
let ty::Adt(adt, _) = checked_ty.kind() else { return; };
if self.tcx.lang_items().range_struct() != Some(adt.did()) {
return;
{
return;
}
+ // Check if start has method named end.
+ let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else { return; };
+ let [hir::PathSegment { ident, .. }] = p.segments else { return; };
+ let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
+ let Ok(_pick) = self.probe_for_name(
+ probe::Mode::MethodCall,
+ *ident,
+ probe::IsSuggestion(true),
+ self_ty,
+ expr.hir_id,
+ probe::ProbeScope::AllTraits,
+ ) else { return; };
+ let mut sugg = ".";
+ let mut span = start.expr.span.between(end.expr.span);
+ if span.lo() + BytePos(2) == span.hi() {
+ // There's no space between the start, the range op and the end, suggest removal which
+ // will be more noticeable than the replacement of `..` with `.`.
+ span = span.with_lo(span.lo() + BytePos(1));
+ sugg = "";
+ }
err.span_suggestion_verbose(
- start.expr.span.between(end.expr.span),
- "you might have meant to write a method call instead of a range",
- ".".to_string(),
- Applicability::MaybeIncorrect,
+ span,
+ "you likely meant to write a method call instead of a range",
+ sugg,
+ Applicability::MachineApplicable,
);
}
}
let suggestion = if let Some((start, end)) = this.diagnostic_metadata.in_range
&& path[0].ident.span.lo() == end.span.lo()
{
+ let mut sugg = ".";
+ let mut span = start.span.between(end.span);
+ if span.lo() + BytePos(2) == span.hi() {
+ // There's no space between the start, the range op and the end, suggest
+ // removal which will look better.
+ span = span.with_lo(span.lo() + BytePos(1));
+ sugg = "";
+ }
Some((
- start.span.between(end.span),
+ span,
"you might have meant to write a method call instead of a range",
- ".".to_string(),
+ sugg.to_string(),
Applicability::MaybeIncorrect,
))
} else if res.is_none() {
struct Type {
option: Option<Vec<u8>>
}
+trait Trait {
+ fn foo(&self) -> Vec<u8>;
+}
+impl Trait for Option<Vec<u8>> {
+ fn foo(&self) -> Vec<u8> {
+ vec![1, 2, 3]
+ }
+}
impl Type {
fn method(&self) -> Option<Vec<u8>> {
self.option..as_ref().map(|x| x)
//~^ ERROR E0308
}
- fn method2(&self) -> Option<Vec<u8>> {
- self.option..foo().map(|x| x)
+ fn method2(&self) -> &u8 {
+ self.option..foo().get(0)
//~^ ERROR E0425
//~| ERROR E0308
}
error[E0425]: cannot find function `foo` in this scope
- --> $DIR/method-access-to-range-literal-typo.rs:14:22
+ --> $DIR/method-access-to-range-literal-typo.rs:22:22
|
-LL | self.option..foo().map(|x| x)
+LL | self.option..foo().get(0)
| ^^^ not found in this scope
|
help: you might have meant to write a method call instead of a range
|
-LL | self.option.foo().map(|x| x)
- | ~
+LL - self.option..foo().get(0)
+LL + self.option.foo().get(0)
+ |
error[E0308]: mismatched types
- --> $DIR/method-access-to-range-literal-typo.rs:10:9
+ --> $DIR/method-access-to-range-literal-typo.rs:18:9
|
LL | fn method(&self) -> Option<Vec<u8>> {
| --------------- expected `Option<Vec<u8>>` because of return type
|
= note: expected enum `Option<_>`
found struct `std::ops::Range<Option<_>>`
-help: you might have meant to write a method call instead of a range
+help: you likely meant to write a method call instead of a range
+ |
+LL - self.option..as_ref().map(|x| x)
+LL + self.option.as_ref().map(|x| x)
|
-LL | self.option.as_ref().map(|x| x)
- | ~
error[E0308]: mismatched types
- --> $DIR/method-access-to-range-literal-typo.rs:14:9
+ --> $DIR/method-access-to-range-literal-typo.rs:22:9
|
-LL | fn method2(&self) -> Option<Vec<u8>> {
- | --------------- expected `Option<Vec<u8>>` because of return type
-LL | self.option..foo().map(|x| x)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, found struct `Range`
+LL | fn method2(&self) -> &u8 {
+ | --- expected `&u8` because of return type
+LL | self.option..foo().get(0)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found struct `Range`
|
- = note: expected enum `Option<_>`
- found struct `std::ops::Range<Option<_>>`
-help: you might have meant to write a method call instead of a range
+ = note: expected reference `&u8`
+ found struct `std::ops::Range<Option<Vec<u8>>>`
+help: you likely meant to write a method call instead of a range
+ |
+LL - self.option..foo().get(0)
+LL + self.option.foo().get(0)
|
-LL | self.option.foo().map(|x| x)
- | ~
error: aborting due to 3 previous errors