Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clippy::new_without_default_derive triggers and shouldn't #3697

Open
vitorenesduarte opened this issue Jan 25, 2019 · 3 comments
Open

clippy::new_without_default_derive triggers and shouldn't #3697

vitorenesduarte opened this issue Jan 25, 2019 · 3 comments
Labels
C-bug Category: Clippy is not doing the correct thing E-medium Call for participation: Medium difficulty level problem and requires some initial experience. I-false-positive Issue: The lint was triggered on code it shouldn't have L-suggestion Lint: Improving, adding or fixing lint suggestions

Comments

@vitorenesduarte
Copy link

vitorenesduarte commented Jan 25, 2019

$ cargo -V
cargo 1.33.0-nightly (907c0febe 2019-01-20)
$ rustc -V
rustc 1.33.0-nightly (01f8e25b1 2019-01-24)
$ cargo clippy -V
clippy 0.0.212 (c5325063 2019-01-25)

Hi. After going through most of chapter 17.3, we end up with something like this:

fn main() {
    let mut post = Post::new();

    post.add_text("Hello");
    assert_eq!("", post.content());

    post.request_review();
    assert_eq!("", post.content());

    post.approve();
    assert_eq!("Hello", post.content());
}

pub struct Post {
    state: Option<Box<dyn State>>,
    content: String,
}

impl Post {
    pub fn new() -> Self {
        Post {
            state: Some(Box::new(Draft {})),
            content: String::new(),
        }
    }

    fn content(&self) -> &str {
        self.state.as_ref().unwrap().content(&self)
    }

    fn add_text(&mut self, text: &str) {
        self.content.push_str(text)
    }

    fn request_review(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.request_review())
        }
    }

    fn approve(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.approve())
        }
    }
}

trait State {
    fn request_review(self: Box<Self>) -> Box<dyn State>;

    fn approve(self: Box<Self>) -> Box<dyn State>;

    fn content<'a>(&self, _post: &'a Post) -> &'a str {
        ""
    }
}

struct Draft {}

impl State for Draft {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        Box::new(PendingReview {})
    }
    
    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }
}

struct PendingReview {}

impl State for PendingReview {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        Box::new(Published {})
    }
}

struct Published {}

impl State for Published {
    fn request_review(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn approve(self: Box<Self>) -> Box<dyn State> {
        self
    }

    fn content<'a>(&self, post: &'a Post) -> &'a str {
        &post.content
    }
}

and if we run cargo clippy, we get a warning that I believe we shouldn't get:

warning: you should consider deriving a `Default` implementation for `Post`
  --> src/main.rs:20:5
   |
20 | /     pub fn new() -> Self {
21 | |         Post {
22 | |             state: Some(Box::new(Draft {})),
23 | |             content: String::new(),
24 | |         }
25 | |     }
   | |_____^
   |
   = note: #[warn(clippy::new_without_default)] on by default
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
help: try this
   |
14 | #[derive(Default)]
   |

Adding #[derive(Default)] to struct Pub without further changes is enough for the warning to disappear (which looks like a bug by itself); and if we replace the body of fn new with Default::default(), as suggested here, we get a different behavior.

(BTW, this warning only triggers if the declarations of both struct Pub and fn new are prefixed with pub, as in the example above).

This might be related with #1579.

@vitorenesduarte
Copy link
Author

A simpler example would be:

fn main() {
    let post = Post::new();
    assert_eq!(0, post.content());
}

pub struct Post {
    state: Option<i32>,
}

impl Post {
    pub fn new() -> Self {
        Post {
            state: Some(0),
        }
    }

    fn content(&self) -> i32 {
        self.state.unwrap()
    }
}

The assert fails since the derived default implementation initializes Post.state with None.

@oli-obk oli-obk added the C-bug Category: Clippy is not doing the correct thing label Jan 25, 2019
@vitorenesduarte vitorenesduarte changed the title clippy:new_without_default_derive triggers and shouldn't clippy::new_without_default_derive triggers and shouldn't Jan 25, 2019
@flip1995
Copy link
Member

A few notes if someone wants to pick this up:

  • new_without_default_derive got merged into new_without_default in Merge new_without_default_derive into new_without_default #3558
  • Linting this is not wrong, the suggestion is. It should suggest to explicitly implement Default instead of deriving it
  • In a more general way: This lint should check if the new function assigns something different than the Default value to the struct fields. If that's the case an explicit implementation of Default is required

@flip1995 flip1995 added E-medium Call for participation: Medium difficulty level problem and requires some initial experience. L-suggestion Lint: Improving, adding or fixing lint suggestions labels Jan 29, 2019
@rail-rain
Copy link
Contributor

Note that the entire function to suggest deriving is removed in #5616 now. I'm not sure whether the flip1995's suggestion should be preserved as a future enhancement for the lint.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: Clippy is not doing the correct thing E-medium Call for participation: Medium difficulty level problem and requires some initial experience. I-false-positive Issue: The lint was triggered on code it shouldn't have L-suggestion Lint: Improving, adding or fixing lint suggestions
Projects
None yet
Development

No branches or pull requests

5 participants