Skip to content

Commit ac46e17

Browse files
authored
Rollup merge of #92569 - George-lewis:87181, r=estebank
Improve Error Messaging for Unconstructed Structs and Enum Variants in Generic Contexts Improves error messaging for empty-tuple structs and enum variants in certain generic contexts. See new ui tests for examples. Closes #87181
2 parents 082e4ca + a6b570b commit ac46e17

17 files changed

+234
-30
lines changed

compiler/rustc_hir/src/hir.rs

+6
Original file line numberDiff line numberDiff line change
@@ -3314,6 +3314,12 @@ impl<'hir> Node<'hir> {
33143314
_ => None,
33153315
}
33163316
}
3317+
3318+
/// Get the fields for the tuple-constructor,
3319+
/// if this node is a tuple constructor, otherwise None
3320+
pub fn tuple_fields(&self) -> Option<&'hir [FieldDef<'hir>]> {
3321+
if let Node::Ctor(&VariantData::Tuple(fields, _)) = self { Some(fields) } else { None }
3322+
}
33173323
}
33183324

33193325
// Some nodes are used a lot. Make sure they don't unintentionally get bigger.

compiler/rustc_typeck/src/check/expr.rs

+25
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ use crate::errors::{
2121
};
2222
use crate::type_error_struct;
2323

24+
use super::suggest_call_constructor;
2425
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
2526
use rustc_ast as ast;
2627
use rustc_data_structures::fx::FxHashMap;
2728
use rustc_data_structures::stack::ensure_sufficient_stack;
2829
use rustc_errors::Diagnostic;
30+
use rustc_errors::EmissionGuarantee;
2931
use rustc_errors::ErrorGuaranteed;
3032
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
3133
use rustc_hir as hir;
@@ -1986,6 +1988,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
19861988
self.tcx().ty_error()
19871989
}
19881990

1991+
fn check_call_constructor<G: EmissionGuarantee>(
1992+
&self,
1993+
err: &mut DiagnosticBuilder<'_, G>,
1994+
base: &'tcx hir::Expr<'tcx>,
1995+
def_id: DefId,
1996+
) {
1997+
let local_id = def_id.expect_local();
1998+
let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id);
1999+
let node = self.tcx.hir().get(hir_id);
2000+
2001+
if let Some(fields) = node.tuple_fields() {
2002+
let kind = match self.tcx.opt_def_kind(local_id) {
2003+
Some(DefKind::Ctor(of, _)) => of,
2004+
_ => return,
2005+
};
2006+
2007+
suggest_call_constructor(base.span, kind, fields.len(), err);
2008+
}
2009+
}
2010+
19892011
fn suggest_await_on_field_access(
19902012
&self,
19912013
err: &mut Diagnostic,
@@ -2055,6 +2077,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
20552077
ty::Opaque(_, _) => {
20562078
self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
20572079
}
2080+
ty::FnDef(def_id, _) => {
2081+
self.check_call_constructor(&mut err, base, def_id);
2082+
}
20582083
_ => {}
20592084
}
20602085

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

+26-12
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_errors::{
88
MultiSpan,
99
};
1010
use rustc_hir as hir;
11+
use rustc_hir::def::DefKind;
1112
use rustc_hir::def_id::DefId;
1213
use rustc_hir::lang_items::LangItem;
1314
use rustc_hir::{ExprKind, Node, QPath};
@@ -29,7 +30,7 @@ use std::cmp::Ordering;
2930
use std::iter;
3031

3132
use super::probe::{Mode, ProbeScope};
32-
use super::{CandidateSource, MethodError, NoMatchData};
33+
use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData};
3334

3435
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3536
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
@@ -488,19 +489,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
488489
}
489490

490491
if self.is_fn_ty(rcvr_ty, span) {
491-
fn report_function<T: std::fmt::Display>(err: &mut Diagnostic, name: T) {
492-
err.note(
493-
&format!("`{}` is a function, perhaps you wish to call it", name,),
494-
);
495-
}
496-
497492
if let SelfSource::MethodCall(expr) = source {
498-
if let Ok(expr_string) = tcx.sess.source_map().span_to_snippet(expr.span) {
499-
report_function(&mut err, expr_string);
500-
} else if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
501-
if let Some(segment) = path.segments.last() {
502-
report_function(&mut err, segment.ident);
493+
let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() {
494+
let local_id = def_id.expect_local();
495+
let hir_id = tcx.hir().local_def_id_to_hir_id(local_id);
496+
let node = tcx.hir().get(hir_id);
497+
let fields = node.tuple_fields();
498+
499+
if let Some(fields) = fields
500+
&& let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) {
501+
Some((fields, of))
502+
} else {
503+
None
503504
}
505+
} else {
506+
None
507+
};
508+
509+
// If the function is a tuple constructor, we recommend that they call it
510+
if let Some((fields, kind)) = suggest {
511+
suggest_call_constructor(expr.span, kind, fields.len(), &mut err);
512+
} else {
513+
// General case
514+
err.span_label(
515+
expr.span,
516+
"this is a function, perhaps you wish to call it",
517+
);
504518
}
505519
}
506520
}

compiler/rustc_typeck/src/check/mod.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,15 @@ pub use check::{check_item_type, check_wf_new};
9898
pub use diverges::Diverges;
9999
pub use expectation::Expectation;
100100
pub use fn_ctxt::*;
101+
use hir::def::CtorOf;
101102
pub use inherited::{Inherited, InheritedBuilder};
102103

103104
use crate::astconv::AstConv;
104105
use crate::check::gather_locals::GatherLocalsVisitor;
105106
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
106-
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
107+
use rustc_errors::{
108+
pluralize, struct_span_err, Applicability, DiagnosticBuilder, EmissionGuarantee, MultiSpan,
109+
};
107110
use rustc_hir as hir;
108111
use rustc_hir::def::Res;
109112
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -988,3 +991,36 @@ fn has_expected_num_generic_args<'tcx>(
988991
generics.count() == expected + if generics.has_self { 1 } else { 0 }
989992
})
990993
}
994+
995+
/// Suggests calling the constructor of a tuple struct or enum variant
996+
///
997+
/// * `snippet` - The snippet of code that references the constructor
998+
/// * `span` - The span of the snippet
999+
/// * `params` - The number of parameters the constructor accepts
1000+
/// * `err` - A mutable diagnostic builder to add the suggestion to
1001+
fn suggest_call_constructor<G: EmissionGuarantee>(
1002+
span: Span,
1003+
kind: CtorOf,
1004+
params: usize,
1005+
err: &mut DiagnosticBuilder<'_, G>,
1006+
) {
1007+
// Note: tuple-structs don't have named fields, so just use placeholders
1008+
let args = vec!["_"; params].join(", ");
1009+
let applicable = if params > 0 {
1010+
Applicability::HasPlaceholders
1011+
} else {
1012+
// When n = 0, it's an empty-tuple struct/enum variant
1013+
// so we trivially know how to construct it
1014+
Applicability::MachineApplicable
1015+
};
1016+
let kind = match kind {
1017+
CtorOf::Struct => "a struct",
1018+
CtorOf::Variant => "an enum variant",
1019+
};
1020+
err.span_label(span, &format!("this is the constructor of {kind}"));
1021+
err.multipart_suggestion(
1022+
"call the constructor",
1023+
vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))],
1024+
applicable,
1025+
);
1026+
}

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ fn main() {
33
let arc = std::sync::Arc::new(oops);
44
//~^ ERROR cannot find value `oops` in this scope
55
//~| NOTE not found
6-
// The error "note: `arc` is a function, perhaps you wish to call it" MUST NOT appear.
6+
// The error "note: this is a function, perhaps you wish to call it" MUST NOT appear.
77
arc.blablabla();
88
//~^ ERROR no method named `blablabla`
99
//~| NOTE method not found
1010
let arc2 = std::sync::Arc::new(|| 1);
11-
// The error "note: `arc2` is a function, perhaps you wish to call it" SHOULD appear
11+
// The error "note: this is a function, perhaps you wish to call it" SHOULD appear
1212
arc2.blablabla();
1313
//~^ ERROR no method named `blablabla`
1414
//~| NOTE method not found
15-
//~| NOTE `arc2` is a function, perhaps you wish to call it
15+
//~| NOTE this is a function, perhaps you wish to call it
1616
}

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn
1414
--> $DIR/fn-help-with-err.rs:12:10
1515
|
1616
LL | arc2.blablabla();
17-
| ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>`
18-
|
19-
= note: `arc2` is a function, perhaps you wish to call it
17+
| ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>`
18+
| |
19+
| this is a function, perhaps you wish to call it
2020

2121
error: aborting due to 3 previous errors
2222

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@ 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-
= note: `Obj::func` is a function, perhaps you wish to call it
5+
| --------- ^ method not found in `fn() -> Ret {Obj::func}`
6+
| |
7+
| this is a function, perhaps you wish to call it
88

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

1717
error: aborting due to 2 previous errors
1818

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ 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)`
5+
| - ^ method not found in `fn(&u8)`
6+
| |
7+
| this is a function, perhaps you wish to call it
68
|
7-
= note: `a` is a function, perhaps you wish to call it
89
= help: items from traits can only be used if the trait is implemented and in scope
910
note: `Trait` defines an item `f`, perhaps you need to implement it
1011
--> $DIR/issue-57362-1.rs:8:1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
struct Bar<T> {
2+
bar: T
3+
}
4+
5+
struct Foo();
6+
impl Foo {
7+
fn foo() { }
8+
}
9+
10+
fn main() {
11+
let thing = Bar { bar: Foo };
12+
thing.bar.foo();
13+
//~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope [E0599]
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope
2+
--> $DIR/empty-tuple-method.rs:12:15
3+
|
4+
LL | thing.bar.foo();
5+
| --------- ^^^ method not found in `fn() -> Foo {Foo}`
6+
| |
7+
| this is the constructor of a struct
8+
|
9+
help: call the constructor
10+
|
11+
LL | (thing.bar)().foo();
12+
| + +++
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0599`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
struct Bar<T> {
2+
bar: T
3+
}
4+
5+
enum Foo{
6+
Tup()
7+
}
8+
impl Foo {
9+
fn foo() { }
10+
}
11+
12+
fn main() {
13+
let thing = Bar { bar: Foo::Tup };
14+
thing.bar.foo();
15+
//~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope [E0599]
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope
2+
--> $DIR/enum-variant.rs:14:15
3+
|
4+
LL | thing.bar.foo();
5+
| --------- ^^^ method not found in `fn() -> Foo {Foo::Tup}`
6+
| |
7+
| this is the constructor of an enum variant
8+
|
9+
help: call the constructor
10+
|
11+
LL | (thing.bar)().foo();
12+
| + +++
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0599`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
struct Bar<T> {
2+
bar: T
3+
}
4+
5+
struct Foo(char, u16);
6+
impl Foo {
7+
fn foo() { }
8+
}
9+
10+
fn main() {
11+
let thing = Bar { bar: Foo };
12+
thing.bar.0;
13+
//~^ ERROR no field `0` on type `fn(char, u16) -> Foo {Foo}` [E0609]
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}`
2+
--> $DIR/tuple-field.rs:12:15
3+
|
4+
LL | thing.bar.0;
5+
| --------- ^
6+
| |
7+
| this is the constructor of a struct
8+
|
9+
help: call the constructor
10+
|
11+
LL | (thing.bar)(_, _).0;
12+
| + +++++++
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0609`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
struct Bar<T> {
2+
bar: T
3+
}
4+
5+
struct Foo(u8, i32);
6+
impl Foo {
7+
fn foo() { }
8+
}
9+
10+
fn main() {
11+
let thing = Bar { bar: Foo };
12+
thing.bar.foo();
13+
//~^ ERROR no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope [E0599]
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope
2+
--> $DIR/tuple-method.rs:12:15
3+
|
4+
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+
| + +++++++
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0599`.

src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ error[E0599]: no method named `call` found for closure `[closure@$DIR/unboxed-cl
22
--> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10
33
|
44
LL | mut_.call((0, ));
5-
| ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:31]`
6-
|
7-
= note: `mut_` is a function, perhaps you wish to call it
5+
| ---- ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:31]`
6+
| |
7+
| this is a function, perhaps you wish to call it
88

99
error: aborting due to previous error
1010

0 commit comments

Comments
 (0)