5

The following code produces one warning but I expected two warnings; one for each time I wrote CString::new("A CString").unwrap().as_ptr():

use std::ffi::CString;
use std::os::raw::c_char;

extern "C" {
    fn puts(s: *const c_char);
}

macro_rules! mymacro {
    () => {
        puts(CString::new("A CString").unwrap().as_ptr());
    };
}

fn main() {
    unsafe {
        puts(CString::new("A CString").unwrap().as_ptr());
        mymacro!();
    }
}
warning: getting the inner pointer of a temporary `CString`
  --> src/main.rs:16:49
   |
16 |         puts(CString::new("A CString").unwrap().as_ptr());
   |              ---------------------------------- ^^^^^^ this pointer will be invalid
   |              |
   |              this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
   |
   = note: `#[warn(temporary_cstring_as_ptr)]` on by default
   = note: pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
   = help: for more information, see https://doc.rust-lang.org/reference/destructors.html

It does evaluate the code twice:

A CString
A CString

What am I misunderstanding?

When I expand the macro, it's clear I have two lines that should emit a warning.

I am using rustc 1.58.1 (db9d1b20b 2022-01-20)

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Cesc
  • 904
  • 1
  • 9
  • 17
  • 1
    Could be a misfiring of [#52467](https://github.com/rust-lang/rust/pull/52467), which should silence warnings from *foreign macros* (macros coming from a different crate). Here the macro is obviously not foreign so it seems like the warning should fire. – Masklinn Mar 02 '22 at 14:11

2 Answers2

3

Edit: An issue was filled about that.


It is not correct about all warnings, e.g. if you'll trigger non_snake_case:

macro_rules! mymacro {
    () => {
        let _XX = ();
    };
}


fn main() {
    let _XX = ();
    mymacro!();
}
warning: variable `_XX` should have a snake case name
 --> src/main.rs:9:9
  |
9 |     let _XX = ();
  |         ^^^ help: convert the identifier to snake case (notice the capitalization): `_xx`
  |
  = note: `#[warn(non_snake_case)]` on by default

warning: variable `_XX` should have a snake case name
  --> src/main.rs:3:13
   |
3  |         let _XX = ();
   |             ^^^ help: convert the identifier to snake case (notice the capitalization): `_xx`
...
10 |     mymacro!();
   |     ---------- in this macro invocation
   |
   = note: this warning originates in the macro `mymacro` (in Nightly builds, run with -Z macro-backtrace for more info)

Playground.

But the temporary_cstring_as_ptr explicitly does not trigger inside macros.

Saddly, I've found no explanation why it's so. This lint used to be on Clippy; this pull request uplifted it to rustc. The Clippy lint looks like it was fired for macros (I haven't tested though); the rustc version doesn't.

Lints are usually suppressed from macros to reduce noise and prevent errors you cannot handle.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
0

Commenting out either line it seems that indeed Rust does not currently warn for the macro call.

I think this is because in general it is not possible to give warnings for all such situations in unsafe code, and so there will be a horizon where a trivial change will affect whether the implementation detects and warns about the dangerous code.

It seems that in this situation the horizon is very short indeed, which may indicate that this area has a relatively low priority and has thus received less attention than other areas of the Rust implementation.

You could also think that the macro call should be "inlined" and so the implementation should see identical code, but apparently that is not the case. The implementation of hygiene may require that the implementation remembers the lexical scope of the macro definition together with the generated code.

hkBst
  • 2,818
  • 10
  • 29