wrenger/bitfield-struct-rs

#![warn(missing_docs)] fires within the proc macro because of missing documentation on new

Closed this issue · 3 comments

When #![warn(missing_docs)] is used, bitfield-struct's new implementation doesn't contain a doc comment which results in the lint firing. For example, the following code causes the lint to fire:

#![warn(missing_docs)]

//! My library.

use bitfield_struct::bitfield;

#[bitfield(u64)]
#[derive(Default)]
pub struct MyBitfield {
    /// Look, a field.
    #[bits(1)]
    pub field_a: bool,
    /// Some field.
    #[bits(1)]
    pub field_b: bool,
    /// Another field.
    #[bits(1)]
    pub field_c: bool,
    /// Currently reserved.
    #[bits(61)]
    pub reserved: u64,
}

Looking at rust-analyzer's recursive expansion, it seems like the new function needs a doc comment for the lint not to fire?

// Recursive expansion of bitfield macro
// ======================================

#[derive(Default, Copy, Clone)]
#[repr(transparent)]
pub struct MyBitfield(u64);

impl MyBitfield {
    pub const fn new() -> Self {
        Self(0)
    }
    const FIELD_A_BITS: usize = 1usize;
    const FIELD_A_OFFSET: usize = 0usize;
    #[doc = " Look, a field."]
    #[doc = "\n\nBits: 0..1"]
    pub fn set_field_a(&mut self, value: bool) {
        *self = self.with_field_a(value);
    }
    #[doc = " Look, a field."]
    #[doc = "\n\nBits: 0..1"]
    pub const fn with_field_a(self, value: bool) -> Self {
        Self(self.0 & !(1 << 0usize) | (value as u64) << 0usize)
    }
    #[doc = " Look, a field."]
    #[doc = "\n\nBits: 0..1"]
    pub const fn field_a(&self) -> bool {
        ((self.0 >> 0usize) & 1) != 0
    }
    const FIELD_B_BITS: usize = 1usize;
    const FIELD_B_OFFSET: usize = 1usize;
    #[doc = " Some field."]
    #[doc = "\n\nBits: 1..2"]
    pub fn set_field_b(&mut self, value: bool) {
        *self = self.with_field_b(value);
    }
    #[doc = " Some field."]
    #[doc = "\n\nBits: 1..2"]
    pub const fn with_field_b(self, value: bool) -> Self {
        Self(self.0 & !(1 << 1usize) | (value as u64) << 1usize)
    }
    #[doc = " Some field."]
    #[doc = "\n\nBits: 1..2"]
    pub const fn field_b(&self) -> bool {
        ((self.0 >> 1usize) & 1) != 0
    }
    const FIELD_C_BITS: usize = 1usize;
    const FIELD_C_OFFSET: usize = 2usize;
    #[doc = " Another field."]
    #[doc = "\n\nBits: 2..3"]
    pub fn set_field_c(&mut self, value: bool) {
        *self = self.with_field_c(value);
    }
    #[doc = " Another field."]
    #[doc = "\n\nBits: 2..3"]
    pub const fn with_field_c(self, value: bool) -> Self {
        Self(self.0 & !(1 << 2usize) | (value as u64) << 2usize)
    }
    #[doc = " Another field."]
    #[doc = "\n\nBits: 2..3"]
    pub const fn field_c(&self) -> bool {
        ((self.0 >> 2usize) & 1) != 0
    }
    const RESERVED_BITS: usize = 61usize;
    const RESERVED_OFFSET: usize = 3usize;
    #[doc = " Currently reserved."]
    #[doc = "\n\nBits: 3..64"]
    pub fn set_reserved(&mut self, value: u64) {
        *self = self.with_reserved(value);
    }
    #[doc = " Currently reserved."]
    #[doc = "\n\nBits: 3..64"]
    pub const fn with_reserved(self, value: u64) -> Self {
        #[allow(unused_comparisons)]
        debug_assert!(if value> = 0 {
      value& !0x1fffffffffffffff =  = 0
    }else {
      !value& !0x1fffffffffffffff =  = 0
    },"value out of bounds");
        Self(
            self.0 & !(0x1fffffffffffffff << 3usize)
                | (value as u64 & 0x1fffffffffffffff) << 3usize,
        )
    }
    #[doc = " Currently reserved."]
    #[doc = "\n\nBits: 3..64"]
    pub const fn reserved(&self) -> u64 {
        let shift = u64::BITS - 61u32;
        (((self.0 >> 3usize) as u64) << shift) >> shift
    }
}
impl From<u64> for MyBitfield {
    fn from(v: u64) -> Self {
        Self(v)
    }
}
impl From<MyBitfield> for u64 {
    fn from(v: MyBitfield) -> u64 {
        v.0
    }
}
impl core::fmt::Debug for MyBitfield {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("MyBitfield")
            .field("field_a", &self.field_a())
            .field("field_b", &self.field_b())
            .field("field_c", &self.field_c())
            .field("reserved", &self.reserved())
            .finish()
    }
}

I've worked around this by putting the structure inside it's own module where the lint is ignored, then pub re-exporting the bitfield, but it would be nice to not to have to do that workaround.

Yes that looks like an oversight. I'll fix that.

Awesome thanks!

New has now docs in 0.4.3 (0b7e87e)