rust-lang / rust-lang/rust-clippy

`clippy::manual_unwrap_or_default` triggers on macro generated code

Open
#12,643 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug I-false-positive
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

Summary

With nightly I have some errors like this in my macro generated code, which can not be easily silenced.

warning: if let can be simplified with `.unwrap_or_default()`
  --> serde_with_macros/src/utils.rs:36:33
   |
36 |     #[darling(rename = "crate", default)]
   |                                 ^^^^^^^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default
   = note: `#[warn(clippy::manual_unwrap_or_default)]` on by default
help: replace it with
   |
36 ~     #[darling(rename = "crate", default)]
37 ~     pub(crate) alt_crate_path.unwrap_or_default())]
   |
Lint Name

clippy::manual_unwrap_or_default

Reproducer

I tried this code:

#[derive(FromDeriveInput)]
#[darling(attributes(serde_with))]
pub(crate) struct DeriveOptions {
    /// Path to the crate
    #[darling(rename = "crate", default)]
    pub(crate) alt_crate_path: Option<Path>,
}

I saw this happen:

warning: if let can be simplified with `.unwrap_or_default()`
  --> serde_with_macros/src/utils.rs:36:33
   |
36 |     #[darling(rename = "crate", default)]
   |                                 ^^^^^^^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default
   = note: `#[warn(clippy::manual_unwrap_or_default)]` on by default
help: replace it with
   |
36 ~     #[darling(rename = "crate", default)]
37 ~     pub(crate) alt_crate_path.unwrap_or_default())]
   |

I expected to see this happen:

No warnings should be caused by the generated code.

The derive macro that generates the Default::default() code looks like this:

impl<'a> ToTokens for DefaultExpression<'a> {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        tokens.append_all(match *self {
            DefaultExpression::Inherit(ident) => {
                let dsn = Ident::new(DEFAULT_STRUCT_NAME, ::proc_macro2::Span::call_site());
                quote!(#dsn.#ident)
            }
            DefaultExpression::Explicit(path) => {
                // Use quote_spanned to properly set the span of the parentheses
                quote_spanned!(path.span()=>#path())
            }
            DefaultExpression::Trait { span } => {
                quote_spanned!(span=> ::darling::export::Default::default())
            }
        });
    }
}

This looks like a reasonable implementation to me. Replacing the Default::default() with unwrap_or_default is not trivially done. It selects the correct code to generate a default value, based on an explicitly given path or implicitly using Default. The darling(default) works very similar to serde(default). The generated code does emit the #[automatically_derived] attribute.

Version
rustc 1.79.0-nightly (aa1c45908 2024-04-06)
binary: rustc
commit-hash: aa1c45908df252a5b0c14e1bcb38c6c55ae02efe
commit-date: 2024-04-06
host: x86_64-unknown-linux-gnu
release: 1.79.0-nightly
LLVM version: 18.1.2
Additional Labels

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the manual_unwrap_or_default lint implementation and run the supplied serde_with_macros/src/utils.rs reproducer. Trace how the lint handles spans from macro-generated code and the #[automatically_derived] attribute; done means the reproducer emits no warning while ordinary applicable code remains diagnosed.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.