Skip to content

Commit 703603a

Browse files
Only suggest call on nonexistent fields and methods if they make sense
1 parent 0734200 commit 703603a

File tree

15 files changed

+94
-171
lines changed

15 files changed

+94
-171
lines changed

compiler/rustc_typeck/src/check/expr.rs

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ use crate::errors::{
2121
};
2222
use crate::type_error_struct;
2323

24-
use super::suggest_call_constructor;
2524
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
2625
use rustc_ast as ast;
2726
use rustc_data_structures::fx::FxHashMap;
@@ -44,7 +43,7 @@ use rustc_middle::middle::stability;
4443
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
4544
use rustc_middle::ty::error::TypeError::FieldMisMatch;
4645
use rustc_middle::ty::subst::SubstsRef;
47-
use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable};
46+
use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable};
4847
use rustc_session::parse::feature_err;
4948
use rustc_span::hygiene::DesugaringKind;
5049
use rustc_span::lev_distance::find_best_match_for_name;
@@ -2280,35 +2279,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22802279
self.tcx().ty_error()
22812280
}
22822281

2283-
fn check_call_constructor(
2284-
&self,
2285-
err: &mut Diagnostic,
2286-
base: &'tcx hir::Expr<'tcx>,
2287-
def_id: DefId,
2288-
) {
2289-
if let Some(local_id) = def_id.as_local() {
2290-
let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id);
2291-
let node = self.tcx.hir().get(hir_id);
2292-
2293-
if let Some(fields) = node.tuple_fields() {
2294-
let kind = match self.tcx.opt_def_kind(local_id) {
2295-
Some(DefKind::Ctor(of, _)) => of,
2296-
_ => return,
2297-
};
2298-
2299-
suggest_call_constructor(base.span, kind, fields.len(), err);
2300-
}
2301-
} else {
2302-
// The logic here isn't smart but `associated_item_def_ids`
2303-
// doesn't work nicely on local.
2304-
if let DefKind::Ctor(of, _) = self.tcx.def_kind(def_id) {
2305-
let parent_def_id = self.tcx.parent(def_id);
2306-
let fields = self.tcx.associated_item_def_ids(parent_def_id);
2307-
suggest_call_constructor(base.span, of, fields.len(), err);
2308-
}
2309-
}
2310-
}
2311-
23122282
fn suggest_await_on_field_access(
23132283
&self,
23142284
err: &mut Diagnostic,
@@ -2378,12 +2348,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23782348
ty::Opaque(_, _) => {
23792349
self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs());
23802350
}
2381-
ty::FnDef(def_id, _) => {
2382-
self.check_call_constructor(&mut err, base, def_id);
2383-
}
23842351
_ => {}
23852352
}
23862353

2354+
self.suggest_fn_call(&mut err, base, base_ty, |output_ty| {
2355+
if let ty::Adt(def, _) = output_ty.kind() && !def.is_enum() {
2356+
def.non_enum_variant().fields.iter().any(|field| {
2357+
field.ident(self.tcx) == ident
2358+
&& field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx)
2359+
})
2360+
} else if let ty::Tuple(tys) = output_ty.kind()
2361+
&& let Ok(idx) = ident.as_str().parse::<usize>()
2362+
{
2363+
idx < tys.len()
2364+
} else {
2365+
false
2366+
}
2367+
});
2368+
23872369
if ident.name == kw::Await {
23882370
// We know by construction that `<expr>.await` is either on Rust 2015
23892371
// or results in `ExprKind::Await`. Suggest switching the edition to 2018.

compiler/rustc_typeck/src/check/method/suggest.rs

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use std::cmp::Ordering;
3131
use std::iter;
3232

3333
use super::probe::{Mode, ProbeScope};
34-
use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData};
34+
use super::{CandidateSource, MethodError, NoMatchData};
3535

3636
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3737
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
@@ -363,44 +363,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
363363
);
364364
}
365365

366-
if self.is_fn_ty(rcvr_ty, span) {
367-
if let SelfSource::MethodCall(expr) = source {
368-
let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() {
369-
if let Some(local_id) = def_id.as_local() {
370-
let hir_id = tcx.hir().local_def_id_to_hir_id(local_id);
371-
let node = tcx.hir().get(hir_id);
372-
let fields = node.tuple_fields();
373-
if let Some(fields) = fields
374-
&& let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) {
375-
Some((fields.len(), of))
376-
} else {
377-
None
378-
}
379-
} else {
380-
// The logic here isn't smart but `associated_item_def_ids`
381-
// doesn't work nicely on local.
382-
if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) {
383-
let parent_def_id = tcx.parent(*def_id);
384-
Some((tcx.associated_item_def_ids(parent_def_id).len(), of))
385-
} else {
386-
None
387-
}
388-
}
389-
} else {
390-
None
391-
};
392-
393-
// If the function is a tuple constructor, we recommend that they call it
394-
if let Some((fields, kind)) = suggest {
395-
suggest_call_constructor(expr.span, kind, fields, &mut err);
396-
} else {
397-
// General case
398-
err.span_label(
399-
expr.span,
400-
"this is a function, perhaps you wish to call it",
401-
);
402-
}
403-
}
366+
if let SelfSource::MethodCall(rcvr_expr) = source {
367+
self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| {
368+
let call_expr = self
369+
.tcx
370+
.hir()
371+
.expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id));
372+
let probe = self.lookup_probe(
373+
span,
374+
item_name,
375+
output_ty,
376+
call_expr,
377+
ProbeScope::AllTraits,
378+
);
379+
probe.is_ok()
380+
});
404381
}
405382

406383
let mut custom_span_label = false;

compiler/rustc_typeck/src/check/mod.rs

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ use check::{check_abi, check_fn, check_mod_item_types};
9696
pub use diverges::Diverges;
9797
pub use expectation::Expectation;
9898
pub use fn_ctxt::*;
99-
use hir::def::CtorOf;
10099
pub use inherited::{Inherited, InheritedBuilder};
101100

102101
use crate::astconv::AstConv;
@@ -960,31 +959,3 @@ fn has_expected_num_generic_args<'tcx>(
960959
generics.count() == expected + if generics.has_self { 1 } else { 0 }
961960
})
962961
}
963-
964-
/// Suggests calling the constructor of a tuple struct or enum variant
965-
///
966-
/// * `snippet` - The snippet of code that references the constructor
967-
/// * `span` - The span of the snippet
968-
/// * `params` - The number of parameters the constructor accepts
969-
/// * `err` - A mutable diagnostic builder to add the suggestion to
970-
fn suggest_call_constructor(span: Span, kind: CtorOf, params: usize, err: &mut Diagnostic) {
971-
// Note: tuple-structs don't have named fields, so just use placeholders
972-
let args = vec!["_"; params].join(", ");
973-
let applicable = if params > 0 {
974-
Applicability::HasPlaceholders
975-
} else {
976-
// When n = 0, it's an empty-tuple struct/enum variant
977-
// so we trivially know how to construct it
978-
Applicability::MachineApplicable
979-
};
980-
let kind = match kind {
981-
CtorOf::Struct => "a struct",
982-
CtorOf::Variant => "an enum variant",
983-
};
984-
err.span_label(span, &format!("this is the constructor of {kind}"));
985-
err.multipart_suggestion(
986-
"call the constructor",
987-
vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))],
988-
applicable,
989-
);
990-
}
Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
11
// This test case checks the behavior of typeck::check::method::suggest::is_fn on Ty::Error.
2+
3+
struct Foo;
4+
5+
trait Bar {
6+
//~^ NOTE `Bar` defines an item `bar`, perhaps you need to implement it
7+
//~| NOTE `Bar` defines an item `bar`, perhaps you need to implement it
8+
fn bar(&self) {}
9+
}
10+
11+
impl Bar for Foo {}
12+
213
fn main() {
314
let arc = std::sync::Arc::new(oops);
415
//~^ ERROR cannot find value `oops` in this scope
516
//~| NOTE not found
6-
// The error "note: this is a function, perhaps you wish to call it" MUST NOT appear.
7-
arc.blablabla();
8-
//~^ ERROR no method named `blablabla`
17+
arc.bar();
18+
//~^ ERROR no method named `bar`
919
//~| NOTE method not found
10-
let arc2 = std::sync::Arc::new(|| 1);
11-
// The error "note: this is a function, perhaps you wish to call it" SHOULD appear
12-
arc2.blablabla();
13-
//~^ ERROR no method named `blablabla`
20+
//~| HELP items from traits can only be used if the trait is implemented and in scope
21+
22+
let arc2 = std::sync::Arc::new(|| Foo);
23+
arc2.bar();
24+
//~^ ERROR no method named `bar`
1425
//~| NOTE method not found
15-
//~| NOTE this is a function, perhaps you wish to call it
26+
//~| HELP items from traits can only be used if the trait is implemented and in scope
27+
//~| HELP use parentheses to call this closure
1628
}

src/test/ui/functions-closures/fn-help-with-err.stderr

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,38 @@
11
error[E0425]: cannot find value `oops` in this scope
2-
--> $DIR/fn-help-with-err.rs:3:35
2+
--> $DIR/fn-help-with-err.rs:14:35
33
|
44
LL | let arc = std::sync::Arc::new(oops);
55
| ^^^^ not found in this scope
66

7-
error[E0599]: no method named `blablabla` found for struct `Arc<_>` in the current scope
8-
--> $DIR/fn-help-with-err.rs:7:9
7+
error[E0599]: no method named `bar` found for struct `Arc<_>` in the current scope
8+
--> $DIR/fn-help-with-err.rs:17:9
99
|
10-
LL | arc.blablabla();
11-
| ^^^^^^^^^ method not found in `Arc<_>`
10+
LL | arc.bar();
11+
| ^^^ method not found in `Arc<_>`
12+
|
13+
= help: items from traits can only be used if the trait is implemented and in scope
14+
note: `Bar` defines an item `bar`, perhaps you need to implement it
15+
--> $DIR/fn-help-with-err.rs:5:1
16+
|
17+
LL | trait Bar {
18+
| ^^^^^^^^^
1219

13-
error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>` in the current scope
14-
--> $DIR/fn-help-with-err.rs:12:10
20+
error[E0599]: no method named `bar` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>` in the current scope
21+
--> $DIR/fn-help-with-err.rs:23:10
22+
|
23+
LL | arc2.bar();
24+
| ^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>`
25+
|
26+
= help: items from traits can only be used if the trait is implemented and in scope
27+
note: `Bar` defines an item `bar`, perhaps you need to implement it
28+
--> $DIR/fn-help-with-err.rs:5:1
29+
|
30+
LL | trait Bar {
31+
| ^^^^^^^^^
32+
help: use parentheses to call this closure
1533
|
16-
LL | arc2.blablabla();
17-
| ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>`
18-
| |
19-
| this is a function, perhaps you wish to call it
34+
LL | arc2().bar();
35+
| ++
2036

2137
error: aborting due to 3 previous errors
2238

src/test/ui/issues/issue-57362-1.stderr

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ error[E0599]: no method named `f` found for fn pointer `fn(&u8)` in the current
22
--> $DIR/issue-57362-1.rs:20:7
33
|
44
LL | a.f();
5-
| - ^ method not found in `fn(&u8)`
6-
| |
7-
| this is a function, perhaps you wish to call it
5+
| ^ method not found in `fn(&u8)`
86
|
97
= help: items from traits can only be used if the trait is implemented and in scope
108
note: `Trait` defines an item `f`, perhaps you need to implement it

src/test/ui/typeck/issue-29124.stderr

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@ error[E0599]: no method named `x` found for fn item `fn() -> Ret {Obj::func}` in
22
--> $DIR/issue-29124.rs:15:15
33
|
44
LL | Obj::func.x();
5-
| --------- ^ method not found in `fn() -> Ret {Obj::func}`
6-
| |
7-
| this is a function, perhaps you wish to call it
5+
| ^ method not found in `fn() -> Ret {Obj::func}`
86

97
error[E0599]: no method named `x` found for fn item `fn() -> Ret {func}` in the current scope
108
--> $DIR/issue-29124.rs:17:10
119
|
1210
LL | func.x();
13-
| ---- ^ method not found in `fn() -> Ret {func}`
14-
| |
15-
| this is a function, perhaps you wish to call it
11+
| ^ method not found in `fn() -> Ret {func}`
1612

1713
error: aborting due to 2 previous errors
1814

src/test/ui/typeck/issue-87181/empty-tuple-method.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ struct Bar<T> {
44

55
struct Foo();
66
impl Foo {
7-
fn foo() { }
7+
fn foo(&self) { }
88
}
99

1010
fn main() {

src/test/ui/typeck/issue-87181/empty-tuple-method.stderr

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the
22
--> $DIR/empty-tuple-method.rs:12:15
33
|
44
LL | thing.bar.foo();
5-
| --------- ^^^ method not found in `fn() -> Foo {Foo}`
6-
| |
7-
| this is the constructor of a struct
5+
| ^^^ method not found in `fn() -> Foo {Foo}`
86
|
9-
help: call the constructor
7+
help: use parentheses to instantiate this tuple struct
108
|
119
LL | (thing.bar)().foo();
1210
| + +++

src/test/ui/typeck/issue-87181/enum-variant.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ enum Foo{
66
Tup()
77
}
88
impl Foo {
9-
fn foo() { }
9+
fn foo(&self) { }
1010
}
1111

1212
fn main() {

src/test/ui/typeck/issue-87181/enum-variant.stderr

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` i
22
--> $DIR/enum-variant.rs:14:15
33
|
44
LL | thing.bar.foo();
5-
| --------- ^^^ method not found in `fn() -> Foo {Foo::Tup}`
6-
| |
7-
| this is the constructor of an enum variant
5+
| ^^^ method not found in `fn() -> Foo {Foo::Tup}`
86
|
9-
help: call the constructor
7+
help: use parentheses to instantiate this tuple variant
108
|
119
LL | (thing.bar)().foo();
1210
| + +++

src/test/ui/typeck/issue-87181/tuple-field.stderr

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@ error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}`
22
--> $DIR/tuple-field.rs:12:15
33
|
44
LL | thing.bar.0;
5-
| --------- ^
6-
| |
7-
| this is the constructor of a struct
5+
| ^
86
|
9-
help: call the constructor
7+
help: use parentheses to instantiate this tuple struct
108
|
119
LL | (thing.bar)(_, _).0;
1210
| + +++++++

src/test/ui/typeck/issue-87181/tuple-method.stderr

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,7 @@ error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}`
22
--> $DIR/tuple-method.rs:12:15
33
|
44
LL | thing.bar.foo();
5-
| --------- ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
6-
| |
7-
| this is the constructor of a struct
8-
|
9-
help: call the constructor
10-
|
11-
LL | (thing.bar)(_, _).foo();
12-
| + +++++++
5+
| ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
136

147
error: aborting due to previous error
158

0 commit comments

Comments
 (0)