Skip to content

Tracking issue for function attribute #[coverage] #84605

Open
0 of 1 issue completed
Open

Description

@richkadel

View all comments

Warning

This main #[coverage(..)] attribute tracking issue description is quite outdated, and likely needs to be revisited.
Please refer to #134749 for the stabilization issue for the #[coverage(..)] attribute.

This issue will track the approval and stabilization of the attribute coverage, needed to give developers a way to "hide" a function from the coverage instrumentation enabled by rustc -Z instrument-coverage.

The Eq trait in the std library implements a marker function that is not meant to be executed, but results in an uncovered region in all rust programs that derive Eq. This attribute will allow the compiler to skip that function, and remove the uncovered regions.

Unresolved questions

  • is it ok that the attribute applied to the crate root, a function, or a module applies to all items within the marked item? So far we do not have such attributes other than the lint level attributes.

Path to stabilization

Please refer to the stabilization issue: #134749.

Activity

  1. richkadel commented on Apr 27, 2021

    @richkadel
    Author
  2. nagisa commented on Apr 27, 2021

    @nagisa
    Member

    Nit: should probably follow the pattern of #[coverage(...)] much like #[inline] and many other attributes.

  3. Swatinem commented on Apr 27, 2021

    @Swatinem
    Contributor

    Just a wish that I have:
    Instead of making this a function level attr, it should work equally on mod or even just blocks.

    Another thing I wish for would be a crate-level attr that would disable coverage for every #[test] fn.

  4. added
    A-code-coverageArea: Source-based code coverage (-Cinstrument-coverage)
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    B-unstableBlocker: Implemented in the nightly compiler and unstable.
    on Apr 28, 2021
  5. added a commit that references this issue on Apr 28, 2021
  6. richkadel commented on Apr 28, 2021

    @richkadel
    Author

    Nit: should probably follow the pattern of #[coverage(...)] much like #[inline] and many other attributes.

    I considered this, and discussed it in the initial PR. I'll copy those comments to this tracking issue, to make it easer to read and reply:

    @tmandry said:

    I also like the idea of combining the first two comments on #84605 by making it #[coverage(always/never)] and making some things (like tests?) disabled by default, but #[coverage(always)] could be used to override this. The only drawbacks I see are that #[coverage] on its own seems meaningless (unlike #[inline]) and these are longer to type.

    The particulars of which things to make covered by default are a little hazy to me at this point, though. Really the problem I want to fix is that assert messages in tests appear uncovered, but that's kind of the point :)

    @richkadel replied:

    I also like the idea of combining the first two comments on #84605 by making it #[coverage(always/never)] and making some things (like tests?) disabled by default

    Yes, I saw the comments, and I do want to consider them. Since this is feature-gated (and fixes a bug), I'd like to land this with no_coverage initially, and follow up with a different PR if we decide to change how it works.

    I did consider something like #[coverage(never)], but (as you point out) I currently feel that #[coverage] or #[coverage(always)] doesn't really make sense to me.

    In fact, I think coverage is especially relevant to tests, so I wouldn't want to disable it for tests by default, or require the user add the attribute in order to get test coverage.

    The only other coverage-related attribute we may want to consider, in the future, is an attribute to enable coverage of unwind paths (#78544). But the MIR InstrumentCoverage pass isn't close to ready to support that yet.

    It's definitely not uncommon for attributes to start with no_, and I just felt that no_coverage is much easier to understand and easier to remember compared to coverage(never) or coverage(none) or coverage(disable). (Also, never might be consistent with the terminology for inline, but the semantics are very different. inline has the notion of "best effort" or "when appropriate" inlining; but those semantics don't apply as well to coverage I think.)

    @tmandry replied:

    Agreed that coverage is relevant to tests, but I don't know if it's very relevant to the test functions themselves. But like I said, there might be a different way of getting at the problem I'm talking about.

    In fact, your reply made me think of an approach I like better, which is removing counters that are along a path that always panics. We don't instrument the full panic paths anyway, and this is a straightforward analysis to do.

    None of this is very relevant to this PR though! I just wanted to put my ideas down somewhere..

  7. richkadel commented on Apr 28, 2021

    @richkadel
    Author

    Just a wish that I have:
    Instead of making this a function level attr, it should work equally on mod or even just blocks.

    Another thing I wish for would be a crate-level attr that would disable coverage for every #[test] fn.

    @Swatinem - Can you expand on this? What are the use cases for disabling coverage at the crate level, or in crate tests?

    For example, the use case addressed in PR #83601 is clear: The implementation of Eq required adding a special marker function (which gets added in every struct that derives from Eq) that was not intended to be called. Every struct that derived from Eq could never achieve 100% coverage. This unexecutable function clearly justifies the requirement for #[no-coverage].

    I personally see the Eq trait issue as an exceptional case.

    Generally speaking...

    I think we want to keep the bar high for now (by restricting the scope of the attribute to functions, until any broader requirement is vetted by the Rust community).

    So if there are other strong cases for excluding coverage instrumentation, please post them here so we can validate the use cases before expanding the scope of the coverage attribute.

  8. richkadel commented on Apr 28, 2021

    @richkadel
    Author

    One more comment for clarity: PR #83601 has been merged and adds the feature-gated #[no_coverage] attribute. The attribute can be enabled on an individual function (by also adding #[feature(no_coverage]) or at the crate level.

  9. richkadel commented on Apr 28, 2021

    @richkadel
    Author

    I should have added: This tracking issue will remain open until the attribute is stablized. This means there is still an opportunity to change the attribute, based on the tracking issue feedback.

    Thanks!

  10. Swatinem commented on Apr 29, 2021

    @Swatinem
    Contributor

    @Swatinem - Can you expand on this? What are the use cases for disabling coverage at the crate level, or in crate tests?

    This might be a bit personal preference, but is also done that way in other language ecosystems.
    You generally want to have coverage for the code under test, not the test code itself. Having coverage metrics for test code provides zero value to me as developer.

  11. nagisa commented on Apr 29, 2021

    @nagisa
    Member

    Note: there are previous initiatives to introduce inheritable/scoped/propagated attributes, e.g. for the optimize attribute, but implementing the propagation behaviour there seemed to be pretty involved to me at the time and IMO any attempts to introduce such attribute propagation mechanisms should be a standalone RFC introducing a mechanism in a more general way.

  12. added a commit that references this issue on Aug 26, 2021
  13. 175 remaining items

  14. loynoir commented on Apr 16, 2025

    @loynoir

    @clarfonthey

    Would there be a #[coverage(expect(off))]?

    Like there is #[allow(rule)] and #[expect(rule)].

    • #[coverage(allow(off))], won't warn when future have coverage

    • #[coverage(expect(off))], will warn when future have coverage

  15. nigeleke commented on Apr 24, 2025

    @nigeleke

    It would be useful to enable some project wide way to coverage(off) on unit tests. I find myself resorting to this on all unit tests. Specifically it's to get around a "missing region" report in llvm-cov for matches!(error, MyErrorEnum::Value(_)) type of tests.

    #[coverage(off)]
    #[cfg(test)]
    mod test {
        use super::*;
    }
  16. tbottomparallel commented on May 23, 2025

    @tbottomparallel

    +1 for the ability to disable coverage for individual blocks. I expect my use of this attribute would almost exclusively be for error handling code for errors which cannot be induced in a test. Some errors that can occur in the field cannot reliably be simulated or induced in unit tests.

    Consider the case of calling tokio::net::UnixDatagram::recv.

    let mut buf = [0u8; 1024];
    match unix_datagram_socket.recv(&mut buf) {
        Ok(_) => (),
        Err(e) => {
            #![coverage(off), reason = "cannot induce recv() error in test"]
            log::error!("failed to recv: {e}");
            // update state / handle error / etc.
        }
    }

    How to make a call to UnixDatagram::recv fail in a test?

    The tokio and std docs for unix datagram sockets don't discuss ways recv can fail at all, so we'll look at the recv(2) manpage instead:

    • tokio handles EAGAIN and EWOULDBLOCK (the underlying unix socket will be in nonblocking mode), and EINTR.
    • EBADF, EFAULT, ENOTSOCK, and EINVAL won't happen because it has a valid file descriptor and buffer.
    • You can't close the file descriptor e.g. libc::close(sock.as_raw_fd()) before calling recv because tokio checks its assumptions and panics if the socket fd is closed. The rust socket code is too robust for these kinds of hacks to work.
    • ECONNREFUSED and ENOTCONN can't happen because this is a connectionless socket.

    That leaves ENOMEM, but a unit test can't reliably exhaust system memory to cause a socket recv call to fail.

    I think this is a good example of why it would be useful to apply this attribute to blocks. The rest of the function (not pictured) is fully unit testable. The error handling code is not unreachable, we can hit it at runtime and we need to handle the error, but I am unaware of any way to unit test it.

  17. clarfonthey commented on May 24, 2025

    @clarfonthey
    Contributor

    We all understand why more granular coverage is useful and do not need more examples. Saying that you want a feature does not make it get developed faster.

  18. shady831213 commented on Feb 13, 2026

    @shady831213

    Hi:
    I have a question about coverage attr...
    consider:

    use core::sync::atomic::AtomicUsize;
    use core::sync::atomic::Ordering;
    static A: AtomicUsize = AtomicUsize::new(0);
    
    #[coverage(off)]
    fn bar() {
    //...
    }
    #[coverage(off)]
    fn foo() {
         //...
         if A.load(lottery::Ordering::Acquire) == 0 {
            bar();
            A.store(1, lottery::Ordering::Release,);
        }
       //...
    }

    my toolchain version is 1.95.0-nightly (0c9e687d2 2026-02-11)

    I expect there is none coverage instrument inserted in function foo(). But I found #[coverage(off)] only take effect for function bar(), but not for load() and store() of AtomicUsize in core lib... So, is there any way to disable coverage for functions in core lib?

    Thanks

  19. haraldreingruber-dedalus commented on Jul 15, 2026

    @haraldreingruber-dedalus

    Maybe #159320 is worth mentioning here.

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-code-coverageArea: Source-based code coverage (-Cinstrument-coverage)B-unstableBlocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-langRelevant to the language team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions