Skip to content

#[must_use] is permitted on functions without return type #54828

Description

@abonander

The following produces no warnings or errors:

#[must_use = "function with no return type"]
fn foo() {}

fn main() {
    foo();
}

Is this intended? Shouldn't it produce a lint warning on the attribute? This is a good mentoring issue, I can bang out instructions once I get an answer (fortunately this doesn't seem to involve hygiene this time so hopefully I should get it right... @petrochenkov)

Activity

  1. added
    A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.
    on Oct 5, 2018
  2. zackmdavis commented on Oct 5, 2018

    @zackmdavis
    Contributor

    The fact that unused-must-use doesn't fire on the foo(); is a consequence of the lint pass returning early for () and !.

    I agree that it's useless to put #[must_use] on a function that returns (), but I don't think anyone thought ahead and decided it should (or shouldn't) be an error. (I wouldn't want to write a dedicated lint for such an edge-case, and I think these days we tend to frown on non-lint warnings on account of their unsilenceability.)

    #48486 is a similar issue with trait implementations.

  3. zackmdavis commented on Oct 5, 2018

    @zackmdavis
    Contributor

    I wouldn't want to write a dedicated lint for such an edge-case

    ... well, come to think of it, I see a good case that this should trigger unused_attributes, as @petrochenkov recently argued with respect to empty #[derive()]s.

  4. Havvy commented on Oct 5, 2018

    @Havvy
    Contributor

    I concur. This should definitely cause unused_attributes to fire.

  5. Centril commented on Oct 6, 2018

    @Centril
    Contributor

    Should the lint unused_attributes also fire for an arbitrary unit type in that case?
    There should be some consistency in the behavior.

  6. hanna-kruppe commented on Oct 6, 2018

    @hanna-kruppe
    Contributor

    By "unit type" do you mean struct Foo;? I'd say no, must_use on user-defined types is always potentially sensible, e.g. ZSTs can be used as tokens for something that the caller should not usually forget to do.

  7. Centril commented on Oct 6, 2018

    @Centril
    Contributor

    @rkruppe I mean any terminal object in the category of Rust types, so struct Foo;, ((), ()), enum Foo { Bar }, and so on.

  8. abonander commented on Oct 6, 2018

    @abonander
    ContributorAuthor

    I would say an explicit () and any arity of tuple that's all ().

  9. Centril commented on Oct 6, 2018

    @Centril
    Contributor

    @abonander including recursively? e.g ((), ((), ()))

  10. abonander commented on Oct 7, 2018

    @abonander
    ContributorAuthor

    Probably, because those types typically don't have any meaning or semantics that can be added to them by the user. I guess it's possible with extension traits but that's going to be a very weird corner case.

  11. Centril commented on Oct 7, 2018

    @Centril
    Contributor

    I'm down with that. It seems to me not too arbitrary a rule.

    On the other hand, if we could get #[must_use] on all types to emit a warning that could work as well.

  12. abonander commented on Oct 7, 2018

    @abonander
    ContributorAuthor

    Like @rkruppe said, custom ZSTs can have semantics attached to them so I don't think it's good to warn on #[must_use] for those.

  13. hanna-kruppe commented on Oct 7, 2018

    @hanna-kruppe
    Contributor

    I was skeptical about linting even () but forgetting to fill in the return type is at least a plausible scenario where such a lint could help. Going any further does not seem likely to help anyone in any scenario. Types like ((), ()) are rarely even constructed much less written in function signatures.

  14. 5 remaining items

  15. abonander commented on Oct 7, 2018

    @abonander
    ContributorAuthor

    @zackmdavis yes but @Centril is talking about the exact opposite behavior, that the unused_must_use lint should trigger regardless of the function's return type.

  16. Centril commented on Oct 7, 2018

    @Centril
    Contributor

    @abonander except for ! and the empty enum.

    As @zackmdavis I'm suggesting that if the #[must_use] annotation has no effect, then a warning should be emitted. But a way to achieve that is for #[must_use] fn foo() {} have an effect. The other way is for #[must_use] fn foo() {} to trigger unused_attributes.

  17. zackmdavis commented on Oct 7, 2018

    @zackmdavis
    Contributor

    We could pull this code out into a common function [...] so that the behavior of #[must_use] and unused-attribute-policing-of-#[must_use] never got out of sync.

    Unfortunately, this turns out to not be easy because the function signature gives us hir::Ty, whereas the UnusedResults pass is looking for a ty::Ty. 💔 😿

    PR forthcoming anyway.

  18. added a commit that references this issue on Oct 7, 2018
    652ac8f
  19. varkor commented on Oct 7, 2018

    @varkor
    Contributor
    • I'd rather functions returning () would be handled by #[must_use] just like any other type.
    • Uninhabited types are irrelevant to the warning, but the current check is wrong regardless and should apply to any uninhabited types: not just ! and empty enums.
  20. pnkfelix commented on Oct 8, 2018

    @pnkfelix
    Contributor

    (deleted comment that was based on, I believe, a misunderstanding of @varkor's comment above.)

  21. varkor commented on Oct 8, 2018

    @varkor
    Contributor

    This is my diagnosis of the issue:

    • (), ! and empty enums are special cased here so that the #![deny(unused_results)] lint wouldn't fire in cases that don't really make sense for it to fire (Unused results lint fails on trivial program #43806).
    • #[must_use] uses the same method to detect unused results, so the same types end up getting special-cased, unintentionally.
    • Therefore, we should just remove the special casing for these types for #[must_use], making this a straightforward bug fix.
    • (Also, special casing ! and empty enums is incorrect, we should be using something like ty.conservative_is_uninhabited() (Less conservative uninhabitedness check #54125).)
  22. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    and removed
    T-langRelevant to the language team
    on Oct 8, 2018
  23. Centril commented on Oct 8, 2018

    @Centril
    Contributor

    Per @varkor's notes re. bug-fix I've relabeled to T-compiler instead.

  24. varkor commented on Oct 8, 2018

    @varkor
    Contributor

    I've submitted #54920 with what I think is the right fix here.

  25. added a commit that references this issue on Oct 9, 2018
  26. added a commit that references this issue on Oct 11, 2018
  27. added a commit that references this issue on Oct 12, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-attributesArea: Attributes (`#[…]`, `#![…]`)A-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions