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

Ensure a sole string-literal passed to panic! is not a fmt string. #24370

Closed
wants to merge 11 commits into from

Conversation

pnkfelix
Copy link
Member

Ensure a sole string-literal passed to panic! is not a fmt string.

To accomplish this, adds ensure_not_fmt_string_literal! macro that will warn during compilation if its expression argument is a fmt string literal.

Since this is making a certain kind of use of panic! illegal, it is a:

[breaking-change]

In particular, a panic like this:

panic!("Is it stringified code: { or is it a ill-formed fmt arg? }");

must be rewritten; one easy rewrite is to add parentheses:

panic!(("Is it stringified code: { or is it a ill-formed fmt arg? }"));

Fix #22932.

@rust-highfive
Copy link
Collaborator

r? @nikomatsakis

(rust_highfive has picked a reviewer for you, use r? to override)

}

pub fn f0_a() {
ensure_not_fmt_string_literal!("`f0_a`", "this does not work {}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this macro be put behind a feature gate as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes good idea.

@alexcrichton
Copy link
Member

This has the potential to be a large-ish breaking change, so it may be useful to gather metrics and see how much breaks. This is a pretty minor problem that may not be worth fixing if it breaks too much.

@pnkfelix
Copy link
Member Author

@bors try

@bors
Copy link
Contributor

bors commented Apr 13, 2015

⌛ Trying commit ac56d7f with merge 2fe4066...

bors added a commit that referenced this pull request Apr 13, 2015
Ensure a sole string-literal passed to `panic!` is not a fmt string.

To accomplish this, adds `ensure_not_fmt_string_literal!` macro that will fail the compile if its expression argument is a fmt string literal.

Since this is making a certain kind of use of `panic!` illegal, it is a:

[breaking-change]

In particular, a panic like this:

```rust
panic!("Is it stringified code: { or is it a ill-formed fmt arg? }");
```

must be rewritten; one easy rewrite is to add parentheses:

```rust
panic!(("Is it stringified code: { or is it a ill-formed fmt arg? }"));
```

----

Fix #22932.
@bors
Copy link
Contributor

bors commented Apr 13, 2015

💔 Test failed - try-mac

@pnkfelix
Copy link
Member Author

@brson argh i'm sorry this clearly does not build. I blame the slow build times on my horribly messed up laptop. :(

@pnkfelix
Copy link
Member Author

@bors try

@bors
Copy link
Contributor

bors commented Apr 13, 2015

⌛ Trying commit 21e580f with merge 2b32efb...

@pnkfelix
Copy link
Member Author

@bors try force

@bors
Copy link
Contributor

bors commented Apr 13, 2015

⌛ Trying commit 08079d5 with merge 0ec30ad...

bors added a commit that referenced this pull request Apr 13, 2015
Ensure a sole string-literal passed to `panic!` is not a fmt string.

To accomplish this, adds `ensure_not_fmt_string_literal!` macro that will fail the compile if its expression argument is a fmt string literal.

Since this is making a certain kind of use of `panic!` illegal, it is a:

[breaking-change]

In particular, a panic like this:

```rust
panic!("Is it stringified code: { or is it a ill-formed fmt arg? }");
```

must be rewritten; one easy rewrite is to add parentheses:

```rust
panic!(("Is it stringified code: { or is it a ill-formed fmt arg? }"));
```

----

Fix #22932.
@bors
Copy link
Contributor

bors commented Apr 13, 2015

💔 Test failed - try-bsd

@pnkfelix
Copy link
Member Author

(I do not yet understand why the try-bsd build failed in the manner above. Maybe I need to look into that.)


@brson was kind enough to run his regression tool on this.

So on the one hand, the reported breakage seems ... pretty minimal? Just two crates on crates.io seem to regress due to this change. In particular:

  • 1st in capnp:
         Compiling capnp v0.2.0 (file:///home/crate)
      src/private/layout.rs:946:9: 947:70 error: panic! input cannot be format string literal
      src/private/layout.rs:946         assert!(element_size != InlineComposite,
      src/private/layout.rs:947                 "Use get_struct_list_{element,field}() for structs");
      error: aborting due to previous error
      Could not compile `capnp`.
  • 2nd in mustache:
         Compiling mustache v0.5.0 (file:///home/crate)
      src/parser.rs:327:26: 327:60 error: panic! input cannot be format string literal
      src/parser.rs:327                 } else { panic!("unbalanced \"{\" in tag"); }
                                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      error: aborting due to previous error
      Could not compile `mustache`.

But on the other hand, looking at this again, I would probably be pretty annoyed to get an outright compile failure for the two cases above, as opposed to a lint warning.

I'm going to see if I can easily rejigger this into a lint instead of a inescapable failure.

@alexcrichton
Copy link
Member

@pnkfelix hm what's the rationale for having this be a lint? As in, when the lint warning fires and I change my code, what benefit is reaped from the modification?

@pnkfelix
Copy link
Member Author

@alexcrichton the benefit is that you have a chance to see that you intended to actually pass arguments.

I cannot stop people who blindly wrap the literal in parens, but at least that stands out visually compared to assert!(cond, "input {} was messed up");

Update: to be clear: The macro being added here would cause a warning to be emitted on the above assert!, and one would be expected to rewrite as either:

assert!(cond, "input {} was messed up", heres_the_input);

or as:

assert!(cond, ("input {} was messed up")); // as in, yes, I really meant `{}` to be printed

@alexcrichton
Copy link
Member

@pnkfelix ah right good point!

@pnkfelix
Copy link
Member Author

@bors try

@bors
Copy link
Contributor

bors commented Apr 15, 2015

🔒 Merge conflict

@pnkfelix
Copy link
Member Author

@bors try

@bors
Copy link
Contributor

bors commented Apr 15, 2015

⌛ Trying commit 12706c9 with merge f637fda...

bors added a commit that referenced this pull request Apr 15, 2015
Ensure a sole string-literal passed to `panic!` is not a fmt string.

To accomplish this, adds `ensure_not_fmt_string_literal!` macro that will fail the compile if its expression argument is a fmt string literal.

Since this is making a certain kind of use of `panic!` illegal, it is a:

[breaking-change]

In particular, a panic like this:

```rust
panic!("Is it stringified code: { or is it a ill-formed fmt arg? }");
```

must be rewritten; one easy rewrite is to add parentheses:

```rust
panic!(("Is it stringified code: { or is it a ill-formed fmt arg? }"));
```

----

Fix #22932.
@bors
Copy link
Contributor

bors commented Apr 15, 2015

💔 Test failed - try-linux

To accomplish this, adds `ensure_not_fmt_string_literal!` macro that
will fail the compile if its argument is a fmt string literal.

`ensure_not_fmt_string_literal!` takes a name along with expr; this
allows for better error messages at its usage sites (like `panic!`).

----

Since this is making a certain kind of use of `panic!` illegal, it
is a:

[breaking-change]

In particular, a panic like this:

```rust
panic!("Is it stringified code: { or is it a ill-formed fmt arg? }");
```

must be rewritten; one easy rewrite is to add parentheses:

```rust
panic!(("Is it stringified code: { or is it a ill-formed fmt arg? }"));
```

----

Fix rust-lang#22932.
As a drive-by, attempt to treat the macro as unstable by having it
emit a reference to an unstable constant in its expansion.  We *do*
allow such an expansion when it occurs within `panic!`, for
discussion, see large comment explaining the hack in the code.
@pnkfelix
Copy link
Member Author

At this point I've tried several different strategies for enforcing and checking the unstable-ness of the helper macro, but none have worked (they either fail to reject the macro, or they reject too much, like invocations of panic!). @huonw and I discussed, and we think the inability to encode this may be symptoms of bugs in the underlying macro system. Not sure yet.

For now, I am planning to just give the helper macro a very ugly, obscure name, and file a bug for investigating the other problems later, but not holding up this PR based on that.

@nikomatsakis
Copy link
Contributor

On Fri, Apr 17, 2015 at 07:06:32AM -0700, Felix S Klock II wrote:

For now, I am planning to just give the helper macro a very ugly, obscure name, and file a bug for investigating the other problems later, but not holding up this PR based on that.

my opinion is we could live with this helper macro indefinitely it push came to shove

@alexcrichton
Copy link
Member

We have a few other macros in the standard library that start with two underscores, and that's generally enough of a deterrent to say "we're not providing guarantees about this"

…s internally.

Unfortunately, i am having issues getting this to actually work.

I repeatedly see:

```
failures:
    [run-fail] run-fail/explicit-panic-msg.rs
```

Namely:

```
failures:

---- [run-fail] run-fail/explicit-panic-msg.rs stdout ----

error: compilation failed!
status: exit code: 101
command: x86_64-apple-darwin/stage2/bin/rustc /Users/fklock/Dev/Mozilla/rust-panic/src/test/run-fail/explicit-panic-msg.rs -L x86_64-apple-darwin/test/run-fail/ --target=x86_64-apple-darwin -L x86_64-apple-darwin/test/run-fail/explicit-panic-msg.stage2-x86_64-apple-darwinlibaux -C prefer-dynamic -o x86_64-apple-darwin/test/run-fail/explicit-panic-msg.stage2-x86_64-apple-darwin --cfg rtopt --cfg debug -L x86_64-apple-darwin/rt
stdout:
------------------------------------------

------------------------------------------
stderr:
------------------------------------------
<std macros>:2:26: 2:57 error: use of unstable library feature 'core': internal to format_args!
<std macros>:2 $ crate:: fmt:: format ( format_args ! ( $ ( $ arg ) * ) ) )
                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: in expansion of __unstable_rustc_ensure_not_fmt_string_literal!
<std macros>:4:1: 4:78 note: expansion site
<std macros>:1:1: 12:23 note: in expansion of panic!
/Users/fklock/Dev/Mozilla/rust-panic/src/test/run-fail/explicit-panic-msg.rs:18:5: 18:37 note: expansion site
<std macros>:2:26: 2:57 help: add #![feature(core)] to the crate attributes to enable
<std macros>:2:26: 2:57 error: use of unstable library feature 'core': internal to format_args!
<std macros>:2 $ crate:: fmt:: format ( format_args ! ( $ ( $ arg ) * ) ) )
                                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: in expansion of __unstable_rustc_ensure_not_fmt_string_literal!
<std macros>:4:1: 4:78 note: expansion site
<std macros>:1:1: 12:23 note: in expansion of panic!
/Users/fklock/Dev/Mozilla/rust-panic/src/test/run-fail/explicit-panic-msg.rs:18:5: 18:37 note: expansion site
<std macros>:2:26: 2:57 help: add #![feature(core)] to the crate attributes to enable
error: aborting due to 2 previous errors

------------------------------------------
```

and I have not yet figured out why this happens even now, nor
how to resolve it.
@nikomatsakis
Copy link
Contributor

I'm not clear on whether this is still waiting for review or what. At this point, it's just a warning, right? So it's not backwards incompatible? The stability code is not really my area of expertise, so I'll change this to:

r? @alexcrichton

@pnkfelix
Copy link
Member Author

Its not done yet, unfortunately; I believe there is still some other fallout I need to address in a manner analogous to b58359a -- but yes, my current plan is for just a warning, which means we probably can land it at any time. (I would like it to end up in the beta nonetheless, but I don't know what our threshold is going to be about what warrants cherry-picking.)

@@ -15,5 +15,6 @@
fn main() {
let mut a = 1;
if 1 == 1 { a = 2; }
panic!(format!("woooo{}", "o"));
let msg = format!("woooo{}", "o");
panic!(msg);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this necessary to change?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah wait I see the commit now, how come this was necessary to change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because otherwise I get stability warnings saying that the internals of float are unstable.

I assume this is due to a bug in either my macro, the expander, or in the way we lint stability via the chain of spans associated with the expansion. I was/am not sure if this is a deal-breaker for this change; maybe I will have to track down that bug first (and presumably let this wait until 1.x to land.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on reflection, the benefit of the warning (catching the programming mistake where one passes a format literal string to panic! but forgets the associated format arguments) does not warrant breaking even code like the example above.

I still think the fact that my macro did not work represents a bug somewhere, perhaps in the macro system or in the stability checker.

But for now I am closing this PR; we can put in this warning later (i.e. after 1.0).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

panic!("{}") shouldn't compile
5 participants