Skip to content

Commit 54b58fc

Browse files
committed
fix #101749, use . instead of :: when accessing a method of an object
1 parent 2ed65da commit 54b58fc

File tree

10 files changed

+214
-8
lines changed

10 files changed

+214
-8
lines changed

compiler/rustc_errors/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ pub enum StashKey {
467467
/// When an invalid lifetime e.g. `'2` should be reinterpreted
468468
/// as a char literal in the parser
469469
LifetimeIsChar,
470+
CallAssocMethod,
470471
}
471472

472473
fn default_track_diagnostic(_: &Diagnostic) {}

compiler/rustc_hir_typeck/src/expr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
527527
self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span);
528528
let ty = match res {
529529
Res::Err => {
530+
self.suggest_assoc_method_call(segs);
530531
let e =
531532
self.tcx.sess.delay_span_bug(qpath.span(), "`Res::Err` but no error emitted");
532533
self.set_tainted_by_errors(e);

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10431043
hir_id: hir::HirId,
10441044
) -> (Ty<'tcx>, Res) {
10451045
let tcx = self.tcx;
1046-
10471046
let path_segs = match res {
10481047
Res::Local(_) | Res::SelfCtor(_) => vec![],
10491048
Res::Def(kind, def_id) => <dyn AstConv<'_>>::def_ids_for_value_path_segments(

compiler/rustc_hir_typeck/src/method/suggest.rs

+60-3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::errors;
55
use crate::FnCtxt;
66
use rustc_ast::ast::Mutability;
77
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8+
use rustc_errors::StashKey;
89
use rustc_errors::{
910
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
1011
MultiSpan,
@@ -13,6 +14,8 @@ use rustc_hir as hir;
1314
use rustc_hir::def::DefKind;
1415
use rustc_hir::def_id::DefId;
1516
use rustc_hir::lang_items::LangItem;
17+
use rustc_hir::PatKind::Binding;
18+
use rustc_hir::PathSegment;
1619
use rustc_hir::{ExprKind, Node, QPath};
1720
use rustc_infer::infer::{
1821
type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
@@ -35,11 +38,11 @@ use rustc_trait_selection::traits::{
3538
FulfillmentError, Obligation, ObligationCause, ObligationCauseCode,
3639
};
3740

38-
use std::cmp::Ordering;
39-
use std::iter;
40-
4141
use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
4242
use super::{CandidateSource, MethodError, NoMatchData};
43+
use rustc_hir::intravisit::Visitor;
44+
use std::cmp::Ordering;
45+
use std::iter;
4346

4447
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4548
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
@@ -1470,6 +1473,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14701473
false
14711474
}
14721475

1476+
/// For code `rect::area(...)`,
1477+
/// if `rect` is a local variable and `area` is a valid assoc method for it,
1478+
/// we try to suggest `rect.area()`
1479+
pub(crate) fn suggest_assoc_method_call(&self, segs: &[PathSegment<'_>]) {
1480+
debug!("suggest_assoc_method_call segs: {:?}", segs);
1481+
if segs.len() != 2 {
1482+
return;
1483+
}
1484+
let (seg1, seg2) = (&segs[0], &segs[1]);
1485+
let Some(mut diag) =
1486+
self.tcx.sess.diagnostic().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod)
1487+
else { return };
1488+
1489+
let map = self.infcx.tcx.hir();
1490+
let body_id = map.body_owned_by(seg1.hir_id.owner.def_id);
1491+
let body = map.body(body_id);
1492+
struct LetVisitor<'a> {
1493+
result: Option<&'a hir::Expr<'a>>,
1494+
ident_name: Symbol,
1495+
}
1496+
1497+
impl<'v> Visitor<'v> for LetVisitor<'v> {
1498+
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
1499+
if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind {
1500+
if let Binding(_, _, ident, ..) = pat.kind &&
1501+
ident.name == self.ident_name {
1502+
self.result = *init;
1503+
}
1504+
}
1505+
hir::intravisit::walk_stmt(self, ex);
1506+
}
1507+
}
1508+
1509+
let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name };
1510+
visitor.visit_body(&body);
1511+
1512+
let parent = self.tcx.hir().get_parent_node(seg1.hir_id);
1513+
if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) &&
1514+
let Some(expr) = visitor.result {
1515+
let self_ty = self.check_expr(expr);
1516+
let probe = self.lookup_probe(
1517+
seg2.ident,
1518+
self_ty,
1519+
call_expr,
1520+
ProbeScope::TraitsInScope,
1521+
);
1522+
if probe.is_ok() {
1523+
diag.emit();
1524+
return;
1525+
}
1526+
}
1527+
diag.cancel();
1528+
}
1529+
14731530
/// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()`
14741531
fn suggest_calling_method_on_field(
14751532
&self,

compiler/rustc_resolve/src/diagnostics.rs

+60-4
Original file line numberDiff line numberDiff line change
@@ -1840,13 +1840,20 @@ impl<'a> Resolver<'a> {
18401840

18411841
(format!("use of undeclared type `{}`", ident), suggestion)
18421842
} else {
1843-
let suggestion = if ident.name == sym::alloc {
1844-
Some((
1843+
// crate or module resolve failed, here we try three things:
1844+
1845+
// 1. special handling for `alloc`
1846+
let mut suggestion = None;
1847+
if ident.name == sym::alloc {
1848+
suggestion = Some((
18451849
vec![],
18461850
String::from("add `extern crate alloc` to use the `alloc` crate"),
18471851
Applicability::MaybeIncorrect,
18481852
))
1849-
} else {
1853+
}
1854+
1855+
// 2. check whether there is a similar name
1856+
suggestion = suggestion.or_else(|| {
18501857
self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module).map(
18511858
|sugg| {
18521859
(
@@ -1856,7 +1863,56 @@ impl<'a> Resolver<'a> {
18561863
)
18571864
},
18581865
)
1859-
};
1866+
});
1867+
1868+
// 3. check whether the name refers to an item in local scope
1869+
// then delay to check whether methond name is an associated function or not
1870+
// For example:
1871+
// ```
1872+
// struct Foo;
1873+
// impl Foo {
1874+
// fn bar(&self) {}
1875+
// }
1876+
//
1877+
// fn main() {
1878+
// let foo = Foo {};
1879+
// foo::bar(); // suggest to foo.bar();
1880+
//}
1881+
//```
1882+
if suggestion.is_none() &&
1883+
let Some(ribs) = ribs &&
1884+
let Some(LexicalScopeBinding::Res(Res::Local(local_id))) = self.resolve_ident_in_lexical_scope(
1885+
ident,
1886+
ValueNS,
1887+
parent_scope,
1888+
None,
1889+
&ribs[ValueNS],
1890+
ignore_binding,
1891+
)
1892+
{
1893+
let sm = self.session.source_map();
1894+
// only check path with two segments, like `foo::bar(..)`
1895+
if let Ok(span) = sm.span_extend_while(ident.span.shrink_to_hi(), |c| c != ' ') &&
1896+
let Ok(snippet) = sm.span_to_snippet(span) &&
1897+
snippet.starts_with("::") && snippet.matches("::").count() == 1 {
1898+
let local_span = *self.pat_span_map.get(&local_id).unwrap();
1899+
let mut err = self.session.struct_span_err(
1900+
ident.span,
1901+
format!(
1902+
"`{}` is not a crate or module",
1903+
ident
1904+
)
1905+
);
1906+
err.span_suggestion_verbose(
1907+
sm.span_extend_while(ident.span.shrink_to_hi(), |c| c == ':').unwrap(),
1908+
"maybe you meant to call instance method",
1909+
".".to_string(),
1910+
Applicability::MaybeIncorrect
1911+
);
1912+
err.span_warn(local_span, format!("ident `{}` is defined at here", ident));
1913+
err.stash(ident.span, rustc_errors::StashKey::CallAssocMethod);
1914+
}
1915+
};
18601916
(format!("use of undeclared crate or module `{}`", ident), suggestion)
18611917
}
18621918
}

src/test/ui/resolve/issue-101749-2.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
struct Rectangle {
2+
width: i32,
3+
height: i32,
4+
}
5+
impl Rectangle {
6+
fn new(width: i32, height: i32) -> Self {
7+
Self { width, height }
8+
}
9+
}
10+
11+
fn main() {
12+
let rect = Rectangle::new(3, 4);
13+
// `area` is not implemented for `Rectangle`, so this should not suggest
14+
let _ = rect::area();
15+
//~^ ERROR failed to resolve: use of undeclared crate or module `rect`
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0433]: failed to resolve: use of undeclared crate or module `rect`
2+
--> $DIR/issue-101749-2.rs:14:13
3+
|
4+
LL | let _ = rect::area();
5+
| ^^^^ use of undeclared crate or module `rect`
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0433`.
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// run-rustfix
2+
struct Rectangle {
3+
width: i32,
4+
height: i32,
5+
}
6+
impl Rectangle {
7+
fn new(width: i32, height: i32) -> Self {
8+
Self { width, height }
9+
}
10+
fn area(&self) -> i32 {
11+
self.height * self.width
12+
}
13+
}
14+
15+
fn main() {
16+
let rect = Rectangle::new(3, 4);
17+
//~^ WARNING ident `rect` is defined at here
18+
let _ = rect.area();
19+
//~^ ERROR failed to resolve: use of undeclared crate or module `rect`
20+
//~| ERROR `rect` is not a crate or module
21+
}

src/test/ui/resolve/issue-101749.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// run-rustfix
2+
struct Rectangle {
3+
width: i32,
4+
height: i32,
5+
}
6+
impl Rectangle {
7+
fn new(width: i32, height: i32) -> Self {
8+
Self { width, height }
9+
}
10+
fn area(&self) -> i32 {
11+
self.height * self.width
12+
}
13+
}
14+
15+
fn main() {
16+
let rect = Rectangle::new(3, 4);
17+
//~^ WARNING ident `rect` is defined at here
18+
let _ = rect::area();
19+
//~^ ERROR failed to resolve: use of undeclared crate or module `rect`
20+
//~| ERROR `rect` is not a crate or module
21+
}
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0433]: failed to resolve: use of undeclared crate or module `rect`
2+
--> $DIR/issue-101749.rs:18:13
3+
|
4+
LL | let _ = rect::area();
5+
| ^^^^ use of undeclared crate or module `rect`
6+
7+
error: `rect` is not a crate or module
8+
--> $DIR/issue-101749.rs:18:13
9+
|
10+
LL | let _ = rect::area();
11+
| ^^^^
12+
|
13+
warning: ident `rect` is defined at here
14+
--> $DIR/issue-101749.rs:16:9
15+
|
16+
LL | let rect = Rectangle::new(3, 4);
17+
| ^^^^
18+
help: maybe you meant to call instance method
19+
|
20+
LL | let _ = rect.area();
21+
| ~
22+
23+
error: aborting due to 2 previous errors
24+
25+
For more information about this error, try `rustc --explain E0433`.

0 commit comments

Comments
 (0)