parent
caa64e5b5e
commit
c9381fc334
5 changed files with 135 additions and 7 deletions
|
@ -1604,6 +1604,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
|
||||||
None,
|
None,
|
||||||
Some(coercion_error),
|
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 {
|
if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
|
||||||
|
|
|
@ -1448,4 +1448,46 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
_ => false,
|
_ => 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ use rustc_ast::ptr::P;
|
||||||
use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
|
use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
|
||||||
use rustc_ast::*;
|
use rustc_ast::*;
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
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::Namespace::{self, *};
|
||||||
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS};
|
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, PartialRes, PerNS};
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
|
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
|
||||||
|
@ -536,6 +536,9 @@ struct DiagnosticMetadata<'ast> {
|
||||||
in_assignment: Option<&'ast Expr>,
|
in_assignment: Option<&'ast Expr>,
|
||||||
is_assign_rhs: bool,
|
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
|
/// If we are currently in a trait object definition. Used to point at the bounds when
|
||||||
/// encountering a struct or enum.
|
/// encountering a struct or enum.
|
||||||
current_trait_object: Option<&'ast [ast::GenericBound]>,
|
current_trait_object: Option<&'ast [ast::GenericBound]>,
|
||||||
|
@ -3320,6 +3323,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn smart_resolve_path_fragment(
|
fn smart_resolve_path_fragment(
|
||||||
&mut self,
|
&mut self,
|
||||||
qself: &Option<P<QSelf>>,
|
qself: &Option<P<QSelf>>,
|
||||||
|
@ -3327,10 +3331,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||||
source: PathSource<'ast>,
|
source: PathSource<'ast>,
|
||||||
finalize: Finalize,
|
finalize: Finalize,
|
||||||
) -> PartialRes {
|
) -> PartialRes {
|
||||||
debug!(
|
|
||||||
"smart_resolve_path_fragment(qself={:?}, path={:?}, finalize={:?})",
|
|
||||||
qself, path, finalize,
|
|
||||||
);
|
|
||||||
let ns = source.namespace();
|
let ns = source.namespace();
|
||||||
|
|
||||||
let Finalize { node_id, path_span, .. } = finalize;
|
let Finalize { node_id, path_span, .. } = finalize;
|
||||||
|
@ -3341,8 +3341,20 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||||
|
|
||||||
let def_id = this.parent_scope.module.nearest_parent_mod();
|
let def_id = this.parent_scope.module.nearest_parent_mod();
|
||||||
let instead = res.is_some();
|
let instead = res.is_some();
|
||||||
let suggestion =
|
let suggestion = if let Some((start, end)) = this.diagnostic_metadata.in_range
|
||||||
if res.is_none() { this.report_missing_type_error(path) } else { None };
|
&& 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 {
|
this.r.use_injections.push(UseError {
|
||||||
err,
|
err,
|
||||||
|
@ -4005,6 +4017,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
||||||
self.visit_expr(rhs);
|
self.visit_expr(rhs);
|
||||||
self.diagnostic_metadata.is_assign_rhs = false;
|
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);
|
visit::walk_expr(self, expr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
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();
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
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`.
|
Loading…
Add table
Add a link
Reference in a new issue