Skip to content

Commit

Permalink
Add new diagnostic
Browse files Browse the repository at this point in the history
  • Loading branch information
George-lewis committed Apr 26, 2022
1 parent 082e4ca commit 14a127b
Show file tree
Hide file tree
Showing 17 changed files with 234 additions and 30 deletions.
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

0 comments on commit 14a127b

Please sign in to comment.