Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Error Messaging for Unconstructed Structs and Enum Variants in Generic Contexts #92569

Merged
merged 2 commits into from
Apr 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3314,6 +3314,12 @@ impl<'hir> Node<'hir> {
_ => None,
}
}

/// Get the fields for the tuple-constructor,
/// if this node is a tuple constructor, otherwise None
pub fn tuple_fields(&self) -> Option<&'hir [FieldDef<'hir>]> {
if let Node::Ctor(&VariantData::Tuple(fields, _)) = self { Some(fields) } else { None }
}
}

// Some nodes are used a lot. Make sure they don't unintentionally get bigger.
Expand Down
25 changes: 25 additions & 0 deletions compiler/rustc_typeck/src/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ use crate::errors::{
};
use crate::type_error_struct;

use super::suggest_call_constructor;
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::Diagnostic;
use rustc_errors::EmissionGuarantee;
use rustc_errors::ErrorGuaranteed;
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
use rustc_hir as hir;
Expand Down Expand Up @@ -1986,6 +1988,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx().ty_error()
}

fn check_call_constructor<G: EmissionGuarantee>(
&self,
err: &mut DiagnosticBuilder<'_, G>,
base: &'tcx hir::Expr<'tcx>,
def_id: DefId,
) {
let local_id = def_id.expect_local();
let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id);
let node = self.tcx.hir().get(hir_id);

if let Some(fields) = node.tuple_fields() {
let kind = match self.tcx.opt_def_kind(local_id) {
Some(DefKind::Ctor(of, _)) => of,
_ => return,
};

suggest_call_constructor(base.span, kind, fields.len(), err);
}
}

fn suggest_await_on_field_access(
&self,
err: &mut Diagnostic,
Expand Down Expand Up @@ -2055,6 +2077,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::Opaque(_, _) => {
self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
}
ty::FnDef(def_id, _) => {
self.check_call_constructor(&mut err, base, def_id);
}
_ => {}
}

Expand Down
38 changes: 26 additions & 12 deletions compiler/rustc_typeck/src/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use rustc_errors::{
MultiSpan,
};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, Node, QPath};
Expand All @@ -29,7 +30,7 @@ use std::cmp::Ordering;
use std::iter;

use super::probe::{Mode, ProbeScope};
use super::{CandidateSource, MethodError, NoMatchData};
use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData};

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

if self.is_fn_ty(rcvr_ty, span) {
fn report_function<T: std::fmt::Display>(err: &mut Diagnostic, name: T) {
err.note(
&format!("`{}` is a function, perhaps you wish to call it", name,),
);
}

if let SelfSource::MethodCall(expr) = source {
if let Ok(expr_string) = tcx.sess.source_map().span_to_snippet(expr.span) {
report_function(&mut err, expr_string);
} else if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
if let Some(segment) = path.segments.last() {
report_function(&mut err, segment.ident);
let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() {
let local_id = def_id.expect_local();
let hir_id = tcx.hir().local_def_id_to_hir_id(local_id);
let node = tcx.hir().get(hir_id);
let fields = node.tuple_fields();

if let Some(fields) = fields
&& let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) {
Some((fields, of))
} else {
None
}
} else {
None
};

// If the function is a tuple constructor, we recommend that they call it
if let Some((fields, kind)) = suggest {
suggest_call_constructor(expr.span, kind, fields.len(), &mut err);
} else {
// General case
err.span_label(
expr.span,
"this is a function, perhaps you wish to call it",
);
}
}
}
Expand Down
38 changes: 37 additions & 1 deletion compiler/rustc_typeck/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,15 @@ pub use check::{check_item_type, check_wf_new};
pub use diverges::Diverges;
pub use expectation::Expectation;
pub use fn_ctxt::*;
use hir::def::CtorOf;
pub use inherited::{Inherited, InheritedBuilder};

use crate::astconv::AstConv;
use crate::check::gather_locals::GatherLocalsVisitor;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
use rustc_errors::{
pluralize, struct_span_err, Applicability, DiagnosticBuilder, EmissionGuarantee, MultiSpan,
};
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def_id::{DefId, LocalDefId};
Expand Down Expand Up @@ -988,3 +991,36 @@ fn has_expected_num_generic_args<'tcx>(
generics.count() == expected + if generics.has_self { 1 } else { 0 }
})
}

/// Suggests calling the constructor of a tuple struct or enum variant
///
/// * `snippet` - The snippet of code that references the constructor
/// * `span` - The span of the snippet
/// * `params` - The number of parameters the constructor accepts
/// * `err` - A mutable diagnostic builder to add the suggestion to
fn suggest_call_constructor<G: EmissionGuarantee>(
span: Span,
kind: CtorOf,
params: usize,
err: &mut DiagnosticBuilder<'_, G>,
) {
// Note: tuple-structs don't have named fields, so just use placeholders
let args = vec!["_"; params].join(", ");
let applicable = if params > 0 {
Applicability::HasPlaceholders
} else {
// When n = 0, it's an empty-tuple struct/enum variant
// so we trivially know how to construct it
Applicability::MachineApplicable
};
let kind = match kind {
CtorOf::Struct => "a struct",
CtorOf::Variant => "an enum variant",
};
err.span_label(span, &format!("this is the constructor of {kind}"));
err.multipart_suggestion(
"call the constructor",
vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))],
applicable,
);
}
6 changes: 3 additions & 3 deletions src/test/ui/functions-closures/fn-help-with-err.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ fn main() {
let arc = std::sync::Arc::new(oops);
//~^ ERROR cannot find value `oops` in this scope
//~| NOTE not found
// The error "note: `arc` is a function, perhaps you wish to call it" MUST NOT appear.
// The error "note: this is a function, perhaps you wish to call it" MUST NOT appear.
arc.blablabla();
//~^ ERROR no method named `blablabla`
//~| NOTE method not found
let arc2 = std::sync::Arc::new(|| 1);
// The error "note: `arc2` is a function, perhaps you wish to call it" SHOULD appear
// The error "note: this is a function, perhaps you wish to call it" SHOULD appear
arc2.blablabla();
//~^ ERROR no method named `blablabla`
//~| NOTE method not found
//~| NOTE `arc2` is a function, perhaps you wish to call it
//~| NOTE this is a function, perhaps you wish to call it
}
6 changes: 3 additions & 3 deletions src/test/ui/functions-closures/fn-help-with-err.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn
--> $DIR/fn-help-with-err.rs:12:10
|
LL | arc2.blablabla();
| ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>`
|
= note: `arc2` is a function, perhaps you wish to call it
| ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>`
| |
| this is a function, perhaps you wish to call it

error: aborting due to 3 previous errors

Expand Down
12 changes: 6 additions & 6 deletions src/test/ui/issues/issue-29124.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ error[E0599]: no method named `x` found for fn item `fn() -> Ret {Obj::func}` in
--> $DIR/issue-29124.rs:15:15
|
LL | Obj::func.x();
| ^ method not found in `fn() -> Ret {Obj::func}`
|
= note: `Obj::func` is a function, perhaps you wish to call it
| --------- ^ method not found in `fn() -> Ret {Obj::func}`
| |
| this is a function, perhaps you wish to call it

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

error: aborting due to 2 previous errors

Expand Down
5 changes: 3 additions & 2 deletions src/test/ui/issues/issue-57362-1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ error[E0599]: no method named `f` found for fn pointer `fn(&u8)` in the current
--> $DIR/issue-57362-1.rs:20:7
|
LL | a.f();
| ^ method not found in `fn(&u8)`
| - ^ method not found in `fn(&u8)`
| |
| this is a function, perhaps you wish to call it
|
= note: `a` is a function, perhaps you wish to call it
= help: items from traits can only be used if the trait is implemented and in scope
note: `Trait` defines an item `f`, perhaps you need to implement it
--> $DIR/issue-57362-1.rs:8:1
Expand Down
14 changes: 14 additions & 0 deletions src/test/ui/typeck/issue-87181/empty-tuple-method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
struct Bar<T> {
bar: T
}

struct Foo();
impl Foo {
fn foo() { }
}

fn main() {
let thing = Bar { bar: Foo };
thing.bar.foo();
//~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope [E0599]
}
16 changes: 16 additions & 0 deletions src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope
--> $DIR/empty-tuple-method.rs:12:15
|
LL | thing.bar.foo();
| --------- ^^^ method not found in `fn() -> Foo {Foo}`
| |
| this is the constructor of a struct
|
help: call the constructor
|
LL | (thing.bar)().foo();
| + +++

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
16 changes: 16 additions & 0 deletions src/test/ui/typeck/issue-87181/enum-variant.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
struct Bar<T> {
bar: T
}

enum Foo{
Tup()
}
impl Foo {
fn foo() { }
}

fn main() {
let thing = Bar { bar: Foo::Tup };
thing.bar.foo();
//~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope [E0599]
}
16 changes: 16 additions & 0 deletions src/test/ui/typeck/issue-87181/enum-variant.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope
--> $DIR/enum-variant.rs:14:15
|
LL | thing.bar.foo();
| --------- ^^^ method not found in `fn() -> Foo {Foo::Tup}`
| |
| this is the constructor of an enum variant
|
help: call the constructor
|
LL | (thing.bar)().foo();
| + +++

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
14 changes: 14 additions & 0 deletions src/test/ui/typeck/issue-87181/tuple-field.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
struct Bar<T> {
bar: T
}

struct Foo(char, u16);
impl Foo {
fn foo() { }
}

fn main() {
let thing = Bar { bar: Foo };
thing.bar.0;
//~^ ERROR no field `0` on type `fn(char, u16) -> Foo {Foo}` [E0609]
}
16 changes: 16 additions & 0 deletions src/test/ui/typeck/issue-87181/tuple-field.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}`
--> $DIR/tuple-field.rs:12:15
|
LL | thing.bar.0;
| --------- ^
| |
| this is the constructor of a struct
|
help: call the constructor
|
LL | (thing.bar)(_, _).0;
| + +++++++

error: aborting due to previous error

For more information about this error, try `rustc --explain E0609`.
14 changes: 14 additions & 0 deletions src/test/ui/typeck/issue-87181/tuple-method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
struct Bar<T> {
bar: T
}

struct Foo(u8, i32);
impl Foo {
fn foo() { }
}

fn main() {
let thing = Bar { bar: Foo };
thing.bar.foo();
//~^ ERROR no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope [E0599]
}
16 changes: 16 additions & 0 deletions src/test/ui/typeck/issue-87181/tuple-method.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope
--> $DIR/tuple-method.rs:12:15
|
LL | thing.bar.foo();
| --------- ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
| |
| this is the constructor of a struct
|
help: call the constructor
|
LL | (thing.bar)(_, _).foo();
| + +++++++

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ error[E0599]: no method named `call` found for closure `[closure@$DIR/unboxed-cl
--> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10
|
LL | mut_.call((0, ));
| ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:31]`
|
= note: `mut_` is a function, perhaps you wish to call it
| ---- ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:31]`
| |
| this is a function, perhaps you wish to call it

error: aborting due to previous error

Expand Down