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

New lint: unit_as_impl_trait #13925

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

samueltardieu
Copy link
Contributor

@samueltardieu samueltardieu commented Jan 1, 2025

This lint implements the suggestion from #13909, and requires that an explicit () is used when a function returns () while declaring an impl trait return type.

I have used specialized messages for three different situations:

  • An empty body
  • A body ending with a statement
  • A body ending with an expression returning ()

One should note that adding the explicit () as suggested will cause the unused_unit lint (default: warn) to trigger. This should be possible to silence it inside functions returning impl traits if needed, to avoid having contradictory suggestions. Thoughts?

Close #13909

changelog: [unit_as_impl_trait]: new lint

@rustbot
Copy link
Collaborator

rustbot commented Jan 1, 2025

r? @llogiq

rustbot has assigned @llogiq.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Jan 1, 2025
@llogiq
Copy link
Contributor

llogiq commented Jan 6, 2025

This should be possible to silence it inside functions returning impl traits if needed, to avoid having contradictory suggestions. Thoughts?

I feel that having () implement traits is a sufficiently weird technique (it's ok for prototype code, but in production, I'd suggest using different zero-sized structs instead) that warning is apropos.

On the other hand, the suggestion is not too helpful in my opinion, given my reasoning stated above.

@samueltardieu
Copy link
Contributor Author

samueltardieu commented Jan 6, 2025

I feel that having () implement traits is a sufficiently weird technique (it's ok for prototype code, but in production, I'd suggest using different zero-sized structs instead) that warning is apropos.

Yes, and as seen in the issue, it exists in production, for example in Axum.

"consider being explicit, and terminate the body with `()`",
);
} else if let Some(last) = block.stmts.last() {
diag.span_note(last.span, "this statement evaluates to `()` because it ends with `;`")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we in this case at least check that the expression the semicolon consumed also implements the required trait? Otherwise I could create an example that won't compile when removing the semicolon, and reducing false positives is always a good thing.

/// if returning `()` is intentional.
#[clippy::version = "1.85.0"]
pub UNIT_AS_IMPL_TRAIT,
suspicious,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that this is warn by default, I'd like to make sure we remove all possible false positives:

  • Whenever the swallowed expression would not impl the returned trait
  • Perhaps there are some #[cfg(..)] shenanigans going on that change the final call's return to () because there's nothing meaningful to return on some systems

diag.span_note(expr.span, "this expression evaluates to `()`")
.span_help(
expr.span.shrink_to_hi(),
"consider being explicit, and terminate the body with `()`",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also the messaging might be shorter:

Suggested change
"consider being explicit, and terminate the body with `()`",
"add `; ()` for explicitness",

"if this is intentional, consider being explicit, and terminate the body with `()`",
);
} else {
diag.span_note(block.span, "the empty body evaluates to `()`")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Especially in this case, having some #[cfg(..)] in the plaintext source would suggest this is intentional.

"this function returns `()` which implements the required trait",
|diag| {
if let Some(expr) = block.expr {
diag.span_note(expr.span, "this expression evaluates to `()`")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it gets tricky, because the expression might return something else on a different target.

@Pzixel
Copy link

Pzixel commented Jan 7, 2025

I feel that having () implement traits is a sufficiently weird technique (it's ok for prototype code, but in production, I'd suggest using different zero-sized structs instead) that warning is apropos.

() implements traits from std, so you can unintentionally use it without any warning, See another example: https://internals.rust-lang.org/t/rust-should-warn-on-impl-trait-returning-unit

And I think it's safe to assume that people in general won't make MyHash and MyEq traits just to avoid this.

@samueltardieu
Copy link
Contributor Author

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Jan 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Lint against return position impl Trait returning ()
4 participants