Skip to content

Commit fb004e9

Browse files
committed
fix #101749, use . instead of :: when accessing a method of an object
1 parent 28a53cd commit fb004e9

15 files changed

+218
-66
lines changed

compiler/rustc_errors/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ pub enum StashKey {
470470
/// Maybe there was a typo where a comma was forgotten before
471471
/// FRU syntax
472472
MaybeFruTypo,
473+
CallAssocMethod,
473474
}
474475

475476
fn default_track_diagnostic(_: &Diagnostic) {}

compiler/rustc_hir_typeck/src/expr.rs

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

compiler/rustc_hir_typeck/src/method/suggest.rs

+61-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,61 @@ 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+
let [seg1, seg2] = segs else { return; };
1482+
let Some(mut diag) =
1483+
self.tcx.sess.diagnostic().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod)
1484+
else { return };
1485+
1486+
let map = self.infcx.tcx.hir();
1487+
let body = map.body(rustc_hir::BodyId { hir_id: self.body_id });
1488+
struct LetVisitor<'a> {
1489+
result: Option<&'a hir::Expr<'a>>,
1490+
ident_name: Symbol,
1491+
}
1492+
1493+
impl<'v> Visitor<'v> for LetVisitor<'v> {
1494+
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
1495+
if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind {
1496+
if let Binding(_, _, ident, ..) = pat.kind &&
1497+
ident.name == self.ident_name {
1498+
self.result = *init;
1499+
}
1500+
}
1501+
hir::intravisit::walk_stmt(self, ex);
1502+
}
1503+
}
1504+
1505+
let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name };
1506+
visitor.visit_body(&body);
1507+
1508+
let parent = self.tcx.hir().get_parent_node(seg1.hir_id);
1509+
if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) &&
1510+
let Some(expr) = visitor.result {
1511+
let self_ty = self.node_ty(expr.hir_id);
1512+
let probe = self.lookup_probe(
1513+
seg2.ident,
1514+
self_ty,
1515+
call_expr,
1516+
ProbeScope::TraitsInScope,
1517+
);
1518+
if probe.is_ok() {
1519+
let sm = self.infcx.tcx.sess.source_map();
1520+
diag.span_suggestion_verbose(
1521+
sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(),
1522+
"you may have meant to call an instance method",
1523+
".".to_string(),
1524+
Applicability::MaybeIncorrect
1525+
);
1526+
}
1527+
}
1528+
diag.emit();
1529+
}
1530+
14731531
/// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()`
14741532
fn suggest_calling_method_on_field(
14751533
&self,

compiler/rustc_resolve/src/diagnostics.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -1840,13 +1840,16 @@ 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+
let mut suggestion = None;
1844+
if ident.name == sym::alloc {
1845+
suggestion = Some((
18451846
vec![],
18461847
String::from("add `extern crate alloc` to use the `alloc` crate"),
18471848
Applicability::MaybeIncorrect,
18481849
))
1849-
} else {
1850+
}
1851+
1852+
suggestion = suggestion.or_else(|| {
18501853
self.find_similarly_named_module_or_crate(ident.name, &parent_scope.module).map(
18511854
|sugg| {
18521855
(
@@ -1856,7 +1859,7 @@ impl<'a> Resolver<'a> {
18561859
)
18571860
},
18581861
)
1859-
};
1862+
});
18601863
(format!("use of undeclared crate or module `{}`", ident), suggestion)
18611864
}
18621865
}

compiler/rustc_resolve/src/late.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -3364,13 +3364,13 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
33643364
// Before we start looking for candidates, we have to get our hands
33653365
// on the type user is trying to perform invocation on; basically:
33663366
// we're transforming `HashMap::new` into just `HashMap`.
3367-
let path = match path.split_last() {
3367+
let prefix_path = match path.split_last() {
33683368
Some((_, path)) if !path.is_empty() => path,
33693369
_ => return Some(parent_err),
33703370
};
33713371

33723372
let (mut err, candidates) =
3373-
this.smart_resolve_report_errors(path, path_span, PathSource::Type, None);
3373+
this.smart_resolve_report_errors(prefix_path, path_span, PathSource::Type, None);
33743374

33753375
// There are two different error messages user might receive at
33763376
// this point:
@@ -3414,11 +3414,23 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
34143414

34153415
if this.should_report_errs() {
34163416
if candidates.is_empty() {
3417-
// When there is no suggested imports, we can just emit the error
3418-
// and suggestions immediately. Note that we bypass the usually error
3419-
// reporting routine (ie via `self.r.report_error`) because we need
3420-
// to post-process the `ResolutionError` above.
3421-
err.emit();
3417+
if path.len() == 2 && prefix_path.len() == 1 {
3418+
// Delay to check whether methond name is an associated function or not
3419+
// ```
3420+
// let foo = Foo {};
3421+
// foo::bar(); // possibly suggest to foo.bar();
3422+
//```
3423+
err.stash(
3424+
prefix_path[0].ident.span,
3425+
rustc_errors::StashKey::CallAssocMethod,
3426+
);
3427+
} else {
3428+
// When there is no suggested imports, we can just emit the error
3429+
// and suggestions immediately. Note that we bypass the usually error
3430+
// reporting routine (ie via `self.r.report_error`) because we need
3431+
// to post-process the `ResolutionError` above.
3432+
err.emit();
3433+
}
34223434
} else {
34233435
// If there are suggested imports, the error reporting is delayed
34243436
this.r.use_injections.push(UseError {
@@ -3427,7 +3439,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
34273439
def_id,
34283440
instead: false,
34293441
suggestion: None,
3430-
path: path.into(),
3442+
path: prefix_path.into(),
34313443
is_call: source.is_call(),
34323444
});
34333445
}

src/test/ui/resolve/bad-module.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
error[E0433]: failed to resolve: use of undeclared crate or module `thing`
2-
--> $DIR/bad-module.rs:2:15
3-
|
4-
LL | let foo = thing::len(Vec::new());
5-
| ^^^^^ use of undeclared crate or module `thing`
6-
71
error[E0433]: failed to resolve: use of undeclared crate or module `foo`
82
--> $DIR/bad-module.rs:5:15
93
|
104
LL | let foo = foo::bar::baz();
115
| ^^^ use of undeclared crate or module `foo`
126

7+
error[E0433]: failed to resolve: use of undeclared crate or module `thing`
8+
--> $DIR/bad-module.rs:2:15
9+
|
10+
LL | let foo = thing::len(Vec::new());
11+
| ^^^^^ use of undeclared crate or module `thing`
12+
1313
error: aborting due to 2 previous errors
1414

1515
For more information about this error, try `rustc --explain E0433`.

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`.
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
let _ = rect.area();
18+
//~^ ERROR failed to resolve: use of undeclared crate or module `rect`
19+
}

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

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
let _ = rect::area();
18+
//~^ ERROR failed to resolve: use of undeclared crate or module `rect`
19+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0433]: failed to resolve: use of undeclared crate or module `rect`
2+
--> $DIR/issue-101749.rs:17:13
3+
|
4+
LL | let _ = rect::area();
5+
| ^^^^ use of undeclared crate or module `rect`
6+
|
7+
help: you may have meant to call an instance method
8+
|
9+
LL | let _ = rect.area();
10+
| ~
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0433`.

src/test/ui/resolve/issue-24968.stderr

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions
2-
--> $DIR/issue-24968.rs:21:19
3-
|
4-
LL | const FOO2: u32 = Self::bar();
5-
| ^^^^ `Self` is only available in impls, traits, and type definitions
6-
7-
error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions
8-
--> $DIR/issue-24968.rs:27:22
9-
|
10-
LL | static FOO_S2: u32 = Self::bar();
11-
| ^^^^ `Self` is only available in impls, traits, and type definitions
12-
131
error[E0411]: cannot find type `Self` in this scope
142
--> $DIR/issue-24968.rs:3:11
153
|
@@ -51,6 +39,18 @@ LL | static FOO_S: Self = 0;
5139
| |
5240
| `Self` not allowed in a static item
5341

42+
error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions
43+
--> $DIR/issue-24968.rs:21:19
44+
|
45+
LL | const FOO2: u32 = Self::bar();
46+
| ^^^^ `Self` is only available in impls, traits, and type definitions
47+
48+
error[E0433]: failed to resolve: `Self` is only available in impls, traits, and type definitions
49+
--> $DIR/issue-24968.rs:27:22
50+
|
51+
LL | static FOO_S2: u32 = Self::bar();
52+
| ^^^^ `Self` is only available in impls, traits, and type definitions
53+
5454
error: aborting due to 7 previous errors
5555

5656
Some errors have detailed explanations: E0411, E0433.

src/test/ui/resolve/typo-suggestion-mistyped-in-path.stderr

+21-21
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
error[E0433]: failed to resolve: could not find `Struc` in `module`
2+
--> $DIR/typo-suggestion-mistyped-in-path.rs:35:13
3+
|
4+
LL | module::Struc::foo();
5+
| ^^^^^
6+
| |
7+
| could not find `Struc` in `module`
8+
| help: a struct with a similar name exists: `Struct`
9+
10+
error[E0599]: no function or associated item named `fob` found for struct `Struct` in the current scope
11+
--> $DIR/typo-suggestion-mistyped-in-path.rs:23:13
12+
|
13+
LL | struct Struct;
14+
| ------------- function or associated item `fob` not found for this struct
15+
...
16+
LL | Struct::fob();
17+
| ^^^
18+
| |
19+
| function or associated item not found in `Struct`
20+
| help: there is an associated function with a similar name: `foo`
21+
122
error[E0433]: failed to resolve: use of undeclared type `Struc`
223
--> $DIR/typo-suggestion-mistyped-in-path.rs:27:5
324
|
@@ -18,15 +39,6 @@ help: there is a crate or module with a similar name
1839
LL | module::foo();
1940
| ~~~~~~
2041

21-
error[E0433]: failed to resolve: could not find `Struc` in `module`
22-
--> $DIR/typo-suggestion-mistyped-in-path.rs:35:13
23-
|
24-
LL | module::Struc::foo();
25-
| ^^^^^
26-
| |
27-
| could not find `Struc` in `module`
28-
| help: a struct with a similar name exists: `Struct`
29-
3042
error[E0433]: failed to resolve: use of undeclared type `Trai`
3143
--> $DIR/typo-suggestion-mistyped-in-path.rs:39:5
3244
|
@@ -36,18 +48,6 @@ LL | Trai::foo();
3648
| use of undeclared type `Trai`
3749
| help: a trait with a similar name exists: `Trait`
3850

39-
error[E0599]: no function or associated item named `fob` found for struct `Struct` in the current scope
40-
--> $DIR/typo-suggestion-mistyped-in-path.rs:23:13
41-
|
42-
LL | struct Struct;
43-
| ------------- function or associated item `fob` not found for this struct
44-
...
45-
LL | Struct::fob();
46-
| ^^^
47-
| |
48-
| function or associated item not found in `Struct`
49-
| help: there is an associated function with a similar name: `foo`
50-
5151
error: aborting due to 5 previous errors
5252

5353
Some errors have detailed explanations: E0433, E0599.

0 commit comments

Comments
 (0)