-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Detect missing ;
on methods with return type ()
#36409
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @eddyb (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
16b0f74
to
a805189
Compare
Thanks for the PR! You know, in looking at this I wonder if we can combine the two. Something like: error[E0308]: mismatched types
--> foo.rs:6:5
|
6 | 3
| ^ expected (), possibly missing `;` at end
|
= note: expected type `()`
= note: found type `{integer}` |
Or maybe error[E0308]: mismatched types
--> foo.rs:6:5
|
6 | 3
| ^ expected type `()`, missing `;` at end?
|
= note: expected type `()`
= note: found type `{integer}` |
One last possibility might be
that is, add another label -- of course this runs afoul of the case where our rendering looks wacky. |
Of the three I vote for #36409 (comment), but take your pick :) |
@jonathandturner I'll use that one, seems to be the least verbose of the options. I'll update this later today. |
a805189
to
96f0ccf
Compare
@jonathandturner I went with @nikomatsakis' style as it lend itself to a cleaner diff and didn't look half bad :) error[E0308]: mismatched types
--> unit-error.rs:6:5
|
6 | 3
| ^
| |
| possibly `;` missing here?
| expected (), found integral variable
|
= note: expected type `()`
= note: found type `{integer}` |
96f0ccf
to
ca3ca82
Compare
nit: I think it reads slightly better in the opposite order
|
Actually, scratch that, either way is fine |
I think it doesn't look half bad in this case:
@jonathandturner I'm puzzled by the test failures. For the test
for the following compiler visual output:
While in master for the same file, the errors emitted are:
That made me think that I might have introduced a double By commenting out If this is indeed the case, does it sound ok if I work around it for now by duplicating the expected error messages in the affected unittests and file a ticket to fix this (either by changing how the emitter works or by introducing deduplication in Also, |
ca3ca82
to
3b4414e
Compare
☔ The latest upstream changes (presumably #36737) made this pull request unmergeable. Please resolve the merge conflicts. |
3b4414e
to
64be372
Compare
@@ -523,7 +523,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { | |||
origin: TypeOrigin, | |||
secondary_span: Option<(Span, String)>, | |||
values: Option<ValuePairs<'tcx>>, | |||
terr: &TypeError<'tcx>) | |||
terr: &TypeError<'tcx>, | |||
implicit_return: bool) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you rename this, and all other instances, to is_block_tail
? It doesn't seem to be related to return
at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eddyb, renamed the field as requested.
a55a299
to
6af8104
Compare
values.expected.is_primitive() && values.found.is_primitive() | ||
let (is_simple_error, expected_unit) = if let &TypeError::Sorts(ref values) = terr { | ||
(values.expected.is_primitive() && values.found.is_primitive(), | ||
&format!("{}", values.expected) == "()") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't .is_unit()
(or I suppose .is_nil()
) work here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is_nil
seems like it would the appropriate method. Running the tests now.
Node::NodeBlock(_) => true, | ||
_ => false, | ||
}; | ||
self.report_mismatched_types_for_return(origin, expected, expr_ty, e, implicit_return); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel a need to question the premise of this change. There's been a lot of debate here about how to tell the user to put a semicolon at the end of this expression. But in the running example, putting a semicolon after the literal "3" is not correct. It results in nonsense code. Maybe we should think deeper about what might be going wrong when a block that is supposed to be terminated with |
@brson it happens to me EDIT: I don't know how often it happens compared to other things. So I weakened the language. =) But it definitely happens. Two caveats though:
This last example again suggests that we might examine the kind of expression to see whether to suggest changing the return type or adding a semicolon. |
I agree with this (unsurprisingly, as I wrote the PR in the first place).
I was just thinking about that very same case, and I think that a reasonable approach to this would be to point out both options, given that we cannot really find a single recommendation that will be right 100% of the time:
|
@estebank - yeah I'm okay with that. An alternative would be to use a suggestion:
|
I've been looking around and by the time the type error is raised I have no longer access to any |
@estebank I believe that if you follow #36409 (comment) you'll regain that ability quite readily. EDIT: Here's what I did. You'd probably want to do something similar to this line. |
Just to say:
👎
👍 |
6f282e1
to
47d50e7
Compare
@eddyb, Oh, my. I completely missed that comment when I got back to this PR. Just pushed a change implementing that recommendation and adding the method return type suggestion. I still need to work on this to avoid suggesting adding a return type when one is already provided, maybe suggesting changing it to the supplied type, and not to suggest anything when it is implementing a trait. |
@GuillaumeGomez done. |
//~| NOTE: possibly missing `;` here? | ||
//~| NOTE: expected (), found bool | ||
//~| NOTE: expected type `()` | ||
//~| NOTE: expected type `()` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder: why these lines are always duplicated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't dig too deeply into this when I noticed it, but we should probably create a ticket for it. Could the same DiagnosticBuilder be emitter multiple times? Maybe that's what's happening...
let trace = TypeTrace { | ||
origin: origin, | ||
values: Types(ExpectedFound { | ||
expected: expected, | ||
found: actual | ||
}) | ||
}; | ||
self.report_and_explain_type_error(trace, &err).emit(); | ||
self.report_and_explain_type_error(trace, &err) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you added this function you should use it wherever report_and_explain_type_error
was used for the same effect.
let mut diag = self.mismatched_types_diag(origin, expected, expr_ty, e.clone()); | ||
|
||
if let Node::NodeBlock(block) = self.tcx.map.get(self.tcx.map.get_parent_node(expr.id)) { | ||
if let TypeError::Sorts(ref values) = e { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can move this to check_block_with_expected
and it will be simpler.
{ | ||
if let Node::NodeItem(func) = self.tcx.map | ||
.get(self.tcx.map.get_parent_node(item.id)) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like this, it's too arbitrary. If you want to do this properly you'd have to add something to Expectation
to indicate that the type comes from a function return type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take a look at the latest code , where I am still against the block's parent to see wether it is a function or a trait's method for the recommendation. Let me know if it still looks too out of place.
I also changed the label to be consistent with the existing phrasing "consider removing this semicolon"/"consider adding a semicolon here".
☔ The latest upstream changes (presumably #37717) made this pull request unmergeable. Please resolve the merge conflicts. |
On a given file `foo.rs`: ```rust fn foo() { return 1; } fn main() { 3 } ``` Provide the following output: ```bash error[E0308]: mismatched types --> foo.rs:2:12 | 1 | fn foo() { | ^ possibly return type `{integer}` missing in this fn? 2 | return 1; | ^ expected (), found integral variable | = note: expected type `()` = note: found type `{integer}` error[E0308]: mismatched types --> foo.rs:6:5 | 6 | 3 | ^ | | | possibly `;` missing here? | expected (), found integral variable | = note: expected type `()` = note: found type `{integer}` error: aborting due to 2 previous errors ```
47d50e7
to
555fb3a
Compare
@estebank @jonathandturner Would it be possible to put the label about |
Btw, two of my comments (#36409 (comment) and #36409 (comment)) appear to still be relevant. |
I believe that I can create a span right after the last char for the expression.
You're right, I didn't have the time to incorporate them over the weekend, just to fix the merge conflict and remove unused code. I will be doing so within the next few days. |
88b4826
to
a45366c
Compare
// Is the block part of a fn? | ||
let parent = self.tcx.map.get(self.tcx.map.get_parent(blk.id)); | ||
let fn_decl = if let Node::NodeItem(&hir::Item { | ||
name, node: hir::ItemFn(ref decl, ..), .. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this is the only part I'm wary about anymore: doing it based solely on syntactical nesting is bound to be either too specific, or wrong. The only way I can see this working as intended is if ExpectHasType
carried the extra information that it corresponds to a ()
implicit return type. cc @rust-lang/compiler
☔ The latest upstream changes (presumably #37965) made this pull request unmergeable. Please resolve the merge conflicts. |
Closing due to inactivity; @estebank if you end up rebasing this, please let us know and we can re-open! |
On a given file
foo.rs
:Provide the following output:
Fixes: #25133