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
This commit is contained in:
commit
ac46e17c02
@ -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.
|
||||
|
@ -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;
|
||||
@ -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,
|
||||
@ -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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -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};
|
||||
@ -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 {
|
||||
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
@ -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,
|
||||
);
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
14
src/test/ui/typeck/issue-87181/empty-tuple-method.rs
Normal file
14
src/test/ui/typeck/issue-87181/empty-tuple-method.rs
Normal file
@ -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
src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
Normal file
16
src/test/ui/typeck/issue-87181/empty-tuple-method.stderr
Normal file
@ -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
src/test/ui/typeck/issue-87181/enum-variant.rs
Normal file
16
src/test/ui/typeck/issue-87181/enum-variant.rs
Normal file
@ -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
src/test/ui/typeck/issue-87181/enum-variant.stderr
Normal file
16
src/test/ui/typeck/issue-87181/enum-variant.stderr
Normal file
@ -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
src/test/ui/typeck/issue-87181/tuple-field.rs
Normal file
14
src/test/ui/typeck/issue-87181/tuple-field.rs
Normal file
@ -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
src/test/ui/typeck/issue-87181/tuple-field.stderr
Normal file
16
src/test/ui/typeck/issue-87181/tuple-field.stderr
Normal file
@ -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
src/test/ui/typeck/issue-87181/tuple-method.rs
Normal file
14
src/test/ui/typeck/issue-87181/tuple-method.rs
Normal file
@ -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
src/test/ui/typeck/issue-87181/tuple-method.stderr
Normal file
16
src/test/ui/typeck/issue-87181/tuple-method.stderr
Normal file
@ -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`.
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user