lambda-fairy / maud

:pencil: Compile-time HTML templates for Rust
https://maud.lambda.xyz
Apache License 2.0
1.98k stars 132 forks source link

Spurious `unreachable_code` warning when `todo!` or `unreachable!` is used inside `html!` #379

Open coreyja opened 1 year ago

coreyja commented 1 year ago

Thanks for the awesome Library! I'm using it in a couple of small projects, and really liking it!

One thing I was realizing today is that it doesn't seem like I can use 'panicking' macros like todo! or unreachable! from within the html! macro, without a warning

For example in my blog I have some code to convert a Markdown AST to HTML using Maud. I wanted to use a match like this to get the correct heading element, but the following gives a warning about unreachable_code. Adding allow[unreachable_code] above the macro just leads to a new warning telling that allow is not needed

Since this is a warning its not the end of the world, but since I usually turn warnings into errors I've worked around this in my application by moving the panics out the macro

html! {
  @match self.depth {
    1 => h1 { (self.content) },
    _ => todo!(),
  }
}
coreyja commented 1 year ago

Of course about 2 minutes after posting this, and using the rust-analyzer expand macro tool I got a version I'm happy with! This compiles without warnings for me!

        html! {
            @match self.depth {
                1 => h1 { (content) },
                2 => h2 { (content) },
                3 => h3 { (content) },
                4 => h4 { (content) },
                5 => h5 { (content) },
                6 => h6 { (content) },
                #[allow(unreachable_code)]
                _ => (unreachable!("Invalid heading depth")),
            }
        }

Feel free to close this issue, if you would like! Thanks again for the awesome tool!

lambda-fairy commented 11 months ago

Hi @coreyja, thanks for the report. I'm curious why this warning comes up. Would you like to share the output of the expand tool?

coreyja commented 11 months ago

This produces the warning

 let output = html! {
    @match self.depth {
    1 => h1 { (content) },
    _ => (unreachable!()),
    }
};

And expands to (I expanded a call to render_to! as well)

let output = {
  extern crate alloc;
  extern crate maud;
  let mut __maud_output = alloc::string::String::with_capacity(70usize);
  match self.depth {
    1 => {
      __maud_output.push_str("<h1>");
      maud::macro_private::render_to!(&content, &mut __maud_output);
      __maud_output.push_str("</h1>");
    }
     _ => {
       {
         use maud::macro_private::*;
         match ChooseRenderOrDisplay(&unreachable!()) {
           x => x
                .implements_render_or_display()
                .render_to(x.0, &mut __maud_output),
         }
       };
      }
    }maud::PreEscaped(__maud_output)
};

I believe this fires the warning because we are using the values of the unreachable to pass into the ChooseRenderOrDisplay struct

Adding the #[allow(unreachable_code)] above that level in the match produces the following and DOES silence the warning: [Left the render_to! unexpanded this time for brevity]

let output = {
  extern crate alloc;
  extern crate maud;
  let mut __maud_output = alloc::string::String::with_capacity(99usize);
  match self.depth {
    1 => {
      __maud_output.push_str("<h1>");
      maud::macro_private::render_to!(&content, &mut __maud_output);
      __maud_output.push_str("</h1>");
    }
    #[allow(unreachable_code)]
    _ => {
      maud::macro_private::render_to!(&unreachable!(), &mut __maud_output);
    }

    }maud::PreEscaped(__maud_output)
};
lambda-fairy commented 6 months ago

I wonder why the warning is triggering on generated code. Marking #[allow(unreachable_code)] will fix the immediate issue, but might hide actual unreachable code (however unlikely that might be). I remember the codegen used to be quite sloppy about spans, so perhaps the real fix is in there.