Fix #65015.
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 {
_ => false,
}
}
+
+ 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) {
+ return;
+ }
+ let hir::ExprKind::Struct(
+ hir::QPath::LangItem(LangItem::Range, ..),
+ [start, end],
+ _,
+ ) = expr.kind else { 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 ty::Adt(adt, _) = checked_ty.kind() else { return; };
+ if self.tcx.lang_items().range_struct() != Some(adt.did()) {
+ return;
+ }
+ if let ty::Adt(adt, _) = expected_ty.kind()
+ && self.tcx.lang_items().range_struct() == Some(adt.did())
+ {
+ return;
+ }
+ 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,
+ );
+ }
}
use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
use rustc_ast::*;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
-use rustc_errors::{DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg};
+use rustc_errors::{Applicability, DiagnosticArgValue, DiagnosticId, IntoDiagnosticArg};
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS};
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
in_assignment: Option<&'ast Expr>,
is_assign_rhs: bool,
+ /// Used to detect possible `.` -> `..` typo when calling methods.
+ in_range: Option<(&'ast Expr, &'ast Expr)>,
+
/// If we are currently in a trait object definition. Used to point at the bounds when
/// encountering a struct or enum.
current_trait_object: Option<&'ast [ast::GenericBound]>,
);
}
+ #[instrument(level = "debug", skip(self))]
fn smart_resolve_path_fragment(
&mut self,
qself: &Option<P<QSelf>>,
source: PathSource<'ast>,
finalize: Finalize,
) -> PartialRes {
- debug!(
- "smart_resolve_path_fragment(qself={:?}, path={:?}, finalize={:?})",
- qself, path, finalize,
- );
let ns = source.namespace();
let Finalize { node_id, path_span, .. } = finalize;
let def_id = this.parent_scope.module.nearest_parent_mod();
let instead = res.is_some();
- let suggestion =
- if res.is_none() { this.report_missing_type_error(path) } else { None };
+ let suggestion = if let Some((start, end)) = this.diagnostic_metadata.in_range
+ && path[0].ident.span.lo() == end.span.lo()
+ {
+ Some((
+ start.span.between(end.span),
+ "you might have meant to write a method call instead of a range",
+ ".".to_string(),
+ Applicability::MaybeIncorrect,
+ ))
+ } else if res.is_none() {
+ this.report_missing_type_error(path)
+ } else {
+ None
+ };
this.r.use_injections.push(UseError {
err,
self.visit_expr(rhs);
self.diagnostic_metadata.is_assign_rhs = false;
}
+ ExprKind::Range(Some(ref start), Some(ref end), RangeLimits::HalfOpen) => {
+ self.diagnostic_metadata.in_range = Some((start, end));
+ self.resolve_expr(start, Some(expr));
+ self.resolve_expr(end, Some(expr));
+ self.diagnostic_metadata.in_range = None;
+ }
_ => {
visit::walk_expr(self, expr);
}
--- /dev/null
+fn as_ref() -> Option<Vec<u8>> {
+ None
+}
+struct Type {
+ option: Option<Vec<u8>>
+}
+
+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)
+ //~^ ERROR E0425
+ //~| ERROR E0308
+ }
+}
+
+fn main() {
+ let _ = Type { option: None }.method();
+}
--- /dev/null
+error[E0425]: cannot find function `foo` in this scope
+ --> $DIR/method-access-to-range-literal-typo.rs:14:22
+ |
+LL | self.option..foo().map(|x| x)
+ | ^^^ 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)
+ | ~
+
+error[E0308]: mismatched types
+ --> $DIR/method-access-to-range-literal-typo.rs:10:9
+ |
+LL | fn method(&self) -> Option<Vec<u8>> {
+ | --------------- expected `Option<Vec<u8>>` because of return type
+LL | self.option..as_ref().map(|x| x)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Option`, 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
+ |
+LL | self.option.as_ref().map(|x| x)
+ | ~
+
+error[E0308]: mismatched types
+ --> $DIR/method-access-to-range-literal-typo.rs:14: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`
+ |
+ = note: expected enum `Option<_>`
+ found struct `std::ops::Range<Option<_>>`
+help: you might have meant to write a method call instead of a range
+ |
+LL | self.option.foo().map(|x| x)
+ | ~
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0308, E0425.
+For more information about an error, try `rustc --explain E0308`.