Skip to content

Zero member tuple struct should warn on incorrect construction #87181

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

Closed
edward-shen opened this issue Jul 16, 2021 · 14 comments · Fixed by #92569
Closed

Zero member tuple struct should warn on incorrect construction #87181

edward-shen opened this issue Jul 16, 2021 · 14 comments · Fixed by #92569
Assignees
Labels
A-diagnostics Area: Messages for errors, warnings, and lints E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@edward-shen
Copy link
Contributor

edward-shen commented Jul 16, 2021

Hey folks, this just bit me so and took a bit to figure out, so I'd thought I'd file an issue for this admittedly niche issue.

Given the following code: playground

trait Foo {
    fn get(&self);
}

struct Bar<T>(T);

struct Inner();

impl Foo for Inner {
    fn get(&self) { }
}

fn main() {
    let thing = Bar(Inner);
    thing.0.get();
}

The current output is:

error[E0599]: no method named `get` found for fn item `fn() -> Inner {Inner}` in the current scope
  --> src/main.rs:16:13
   |
16 |     thing.0.get();
   |             ^^^ method not found in `fn() -> Inner {Inner}`
   |
   = note: `thing.0` 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: `Foo` defines an item `get`, perhaps you need to implement it
  --> src/main.rs:2:1
   |
2  | trait Foo {
   | ^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

Ideally the output should look like:

error[E0599]: no method named `get` found for fn item `fn() -> Inner {Inner}` in the current scope
  --> src/main.rs:16:13
   |
16 |     thing.0.get();
   |             ^^^ method not found in `fn() -> Inner {Inner}`
   |
   = note: `thing.0` is a function, perhaps you wish to call it
   = note: `Inner` is a tuple struct with zero fields, perhaps you wish to initialize it instead: `Bar(Inner())`
   = help: items from traits can only be used if the trait is implemented and in scope
note: `Foo` defines an item `get`, perhaps you need to implement it
  --> src/main.rs:2:1
   |
2  | trait Foo {
   | ^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0599`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

This was an extremely confusing error message for me, as I suddenly had a fn() -> Inner type out of no where in my code! Of course, this was a simplified issue, but it's not immediately obvious that 1. a unit struct is actually a function returning its own type, and 2. the problem was due to the fact that a unit struct is not the same as a zero-member tuple struct.

This was extracted out of a much more complex error message, where the only hint to the actual problem was that the unit struct "constructor" didn't satisfy the trait bound, which pointed to a construction issue.

Not quite sure whether or not it's possible to actually implement a suggestion as written, but perhaps even a check between Inner vs Inner() might be a good note to have?

@edward-shen edward-shen added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jul 16, 2021
@estebank
Copy link
Contributor

estebank commented Jul 16, 2021

We should suggest writing (self.0)() like we already do if you write self.0().

You'll want to look at this file

let mut err = struct_span_err!(
tcx.sess,
assoc_ident.span,
E0599,
"no variant named `{}` found for enum `{}`",
assoc_ident,
qself_ty,
);
let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT");
if let Some(suggested_name) = find_best_match_for_name(
&adt_def
.variants
.iter()
.map(|variant| variant.ident.name)
.collect::<Vec<Symbol>>(),
assoc_ident.name,
None,
) {
err.span_suggestion(
assoc_ident.span,
"there is a variant with a similar name",
suggested_name.to_string(),
Applicability::MaybeIncorrect,
);
} else {
err.span_label(
assoc_ident.span,
format!("variant not found in `{}`", qself_ty),
);
}
if let Some(sp) = tcx.hir().span_if_local(adt_def.did) {
let sp = tcx.sess.source_map().guess_head_span(sp);
err.span_label(sp, format!("variant `{}` not found here", assoc_ident));
}
err.emit();

and a check to see if qself_ty is a closure. I believe that hir_ref_id is the node for thing.0.get(), but I am not certain. If it is, then you can use tcx.hir().find(hir_ref_id) to get the hir::Node, from which you can decompose it to get the span for thing.0, and use it for a multipart_suggestion to suggest the addition of parentheses. We need not worry about the case where instead of thing.0.get() we have thing.some_method.get(), as it is already properly handled.

@estebank estebank added E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. labels Jul 16, 2021
@Rustin170506 Rustin170506 removed their assignment Jul 17, 2021
@Rustin170506
Copy link
Member

@rustbot claim

@Rustin170506 Rustin170506 removed their assignment Jul 25, 2021
@JaydenElliott
Copy link

JaydenElliott commented Jul 26, 2021

@rustbot claim

@rustbot
Copy link
Collaborator

rustbot commented Jul 26, 2021

Error: Parsing assign command in comment failed: ...'tbot claim' | error: expected end of command at >| ' Happy to '...

Please let @rust-lang/release know if you're having trouble with this bot.

@JaydenElliott
Copy link

JaydenElliott commented Jul 26, 2021

Happy to try give this a go as my first contribution. I have a few questions, can I message you on zulip @estebank?

@estebank
Copy link
Contributor

@JaydenElliott Yes! Feel free to ping me on zulip.

@inashivb
Copy link
Contributor

inashivb commented Sep 9, 2021

Hi @JaydenElliott !
Are you still working on this issue? (I am looking for issues to work on so just checking in :) )

@JaydenElliott
Copy link

Hi @inashivb, I think this one was a bit too difficult for me, so feel free to take it off of me!

@JaydenElliott JaydenElliott removed their assignment Sep 9, 2021
@inashivb
Copy link
Contributor

Thank you very much, @JaydenElliott ! I'm not sure if I'll be able to do it either but I'd like to give it a try. I hope you find a new issue to work on soon. :)

@inashivb
Copy link
Contributor

@rustbot claim

@George-lewis
Copy link
Contributor

Hello everyone, this issue seems to have stalled, so I think I'll take a crack at it myself if that's okay

@George-lewis
Copy link
Contributor

@rustbot claim

@rustbot rustbot assigned George-lewis and unassigned inashivb Jan 5, 2022
@George-lewis
Copy link
Contributor

It took me a lot of digging, but I've landed in rustc_typeck::check::method::suggest::report_method_error. This function is called when there was an error around a method, including when it's not found. So, in the case where the method isn't found, I check if the receiver is a no-argument constructor and apply the suggestion. I'm still working on it, but I think I'll open a PR soon-ish

@George-lewis
Copy link
Contributor

George-lewis commented Jan 5, 2022

I've found a related issue for field access. It can be seen here:

struct Bar<T>(T);

struct Inner(u8);

fn main() {
    let thing = Bar(Inner);
    thing.0.0();
}

Which yields: error[E0609]: no field 0on typefn(u8) -> Inner {Inner}``

We could probably do something similar to what we're doing here, and tell them to construct the struct/enum variant? Does anyone know if there's an issue for this, and can I fix it here?

Edit: Thinking about this a little bit more, it appears that this shows up in a few cases, like where the member of Bar is a closure of fn ptr. Maybe we should just always include a note to suggest calling it?

@bors bors closed this as completed in ac46e17 Apr 27, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue. E-mentor Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
7 participants