Invalid clippy `missing_const_for_fn`

upgraded to rust 1.86.0. now i'm getting some new missing_const_for_fn lints, but when i add const i get a compiler error. Am I missing something dumb here? Known issue? Am I holding it wrong? Here's a couple examples:

  // ⚠️ clippy: this could be a `const fn`
  pub fn authors(&self) -> &[Author] {
    &self.authors
  }

..adding const produces:

 1  error[E0015]: cannot perform non-const deref coercion on `std::vec::Vec<types::Author>` in constant functions
     --> core/src/document_meta.rs:92:5
      |
 92   |     &self.authors
      |     ^^^^^^^^^^^^^
      |
      = note: attempting to deref into `[types::Author]`

here's another:

  // ⚠️ clippy: this could be a `const fn`
  pub fn str(&self) -> Option<&str> {
    match self {
      AttrValue::String(s) => Some(s),
      AttrValue::Bool(true) => Some(""),
      AttrValue::Bool(false) => None,
    }
  }

which produces:

 1  error[E0015]: cannot perform non-const deref coercion on `std::string::String` in constant functions
     --> core/src/attrs.rs:22:36
      |
 22   |       AttrValue::String(s) => Some(s),
      |                                    ^
      |
      = note: attempting to deref into `str`

Don't have a specific answer to your question, can only say that I've been seeing this as well.

We have a convention where if a lint suggestion causes breakage we insert an allow rule to silence the lint and "tag" it with a // ToDo: broken-lint comment so we can find them again, and I've been adding a few broken-lint tags lately due to const fn suggestions.

missing_const_for_fn is a lint in the nursery category, which is not enabled by default. Lints in that category often have false positives. If you don't want to live with such false positives, you should not enable the lint or the category (whichever you did).

1 Like

You can suppress lints (e.g., #[expect(clippy::missing_const_for_fn, reason = "false positive"]) on a per-function basis. I'm in the (understandable) minority that typically enables all lint groups and separately expect the ones I disagree with for library crates like below:

#![deny(
    unknown_lints,
    future_incompatible,
    let_underscore,
    missing_docs,
    nonstandard_style,
    refining_impl_trait,
    rust_2018_compatibility,
    rust_2018_idioms,
    rust_2021_compatibility,
    rust_2024_compatibility,
    unsafe_code,
    unused,
    warnings,
    clippy::all,
    clippy::cargo,
    clippy::complexity,
    clippy::correctness,
    clippy::nursery,
    clippy::pedantic,
    clippy::perf,
    clippy::restriction,
    clippy::style,
    clippy::suspicious
)]
#![expect(
    clippy::arbitrary_source_item_ordering,
    clippy::blanket_clippy_restriction_lints,
    clippy::exhaustive_enums,
    clippy::exhaustive_structs,
    clippy::implicit_return,
    clippy::min_ident_chars,
    clippy::missing_trait_methods,
    clippy::multiple_crate_versions,
    clippy::pub_with_shorthand,
    clippy::pub_use,
    clippy::ref_patterns,
    clippy::return_and_then,
    clippy::self_named_module_files,
    clippy::single_call_fn,
    clippy::single_char_lifetime_names,
    clippy::unseparated_literal_suffix,
    reason = "noisy, opinionated, and likely doesn't prevent bugs or improve APIs"
)]

You can try beta to see if the issue is fixed. Perhaps this PR fixed it. If beta or at least nightly doesn't have this fixed, then please raise an issue on GitHub.

In case you weren't already aware, you can specify crate-level lint configuration in the [lints] section of Cargo.toml now. Your deny-list should probably be in the manifest, especially if it's the same for every crate you author.

The expect-list is crate-specific, so makes sense to live in lib.rs… but I'd probably use allow in [lints] for the pedantic lints you just disagree with, and only use crate level expect for false positives, so you'll know to remove the expect if/when those are fixed.

allow is for lints you don't care about. expect is for lints where it not firing is something to lint about.

(I also disagree with setting the default lint level to deny; there's no good reason to block local builds and tests on lint failures. Warn in local development, block CI merge on lint failures.)

2 Likes

That's a great idea. I'll probably start migrating to using [lints].

(I also disagree with setting the default lint level to deny; there's no good reason to block local builds and tests on lint failures. Warn in local development, block CI merge on lint failures.)

I didn't want this to turn into a debate about how globally denying lints (especially lint groups) is "wrong" which is why I used the parenthetical adjective "understandable". I've read arguments against it, but I haven't been convinced yet primarily due to the fact that cargo uses --cap-lints so such an aggressive stance isn't "viral" that breaks code that depends on the library. I have long ago accepted that I'm a "weirdo", so I don't expect or even encourage others to do some of the things I do.

I do the exact opposite for binary crates (i.e., I allow all lints) before publishing to crates.io or cutting a release tag since users are expected to build the binary directly, and I don't want the build to fail or even to annoy the user with a bunch of warnings.

I do think your recommendation of "block CI merge on lint failures" is not quite "good enough" since I've very rarely have found bugs in code due to a new lint without any changes to the source code. This means you'd also want to add something to the CI that re-builds the code with each new rustc version so you're not handcuffed to just merges.