Why is a warning suppressed when the code is inside of a macro?

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)

PS.: I asked the same question in stackoverflow but still looking for answers

It's very common for warnings to not run in macro-generated code, because they're usually unhelpful there. The most obvious case is something like unneeded parens -- while they might not be needed in this expansion it's possible they're needed in others. And, more generally, telling the user of a macro about some problem with the macro-generated code is often unhelpful, since they might not be able to change the expansion anyway.

5 Likes

It makes sense to have good defaults that are helpful most of the time.

In our case the users of a macro and the ones developing the macro are the same team, thus we are able to change the expansion, maybe not a common use case.

Is there a way for us to tell the compiler to be more strict about macro generated code?

I guess it's because the compilation of rust has different stages, and each stage performs different lint checking.

The stages are:

  1. AST(=Series of tokens)
  2. High-level IR
  3. Typed High-level IR
  4. Middle-level IR
  5. LLVM IR
  6. binary

Given a particular lint option or optimization stage, It's hard to figure out which stage conducts that lint/optimization.

According to docs, some lints aim at an early stage of compilation or even before that.

Macro expansion usually happens on the High-level IR stage. Therefore, if a lint 'temporary_csting_as_ptr' is set to be before the expansion, the lint cannot capture codes that are generated after the macro expansion.

As far as I know, lint-checking around the extern library usually comes in the early stage of compilation due to dependency. So the lint may miss lines that are not directly exposed to global context like macro does

1 Like

(Somwhat a cross-post of my answer at StackOverflow).

This lint intentionally does that. Not all lints do, e.g. non_snake_case:

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

fn main() {
    mymacro!();
}
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`
...
9 |     mymacro!();
  |     ---------- in this macro invocation
  |
  = note: `#[warn(non_snake_case)]` on by default
  = note: this warning originates in the macro `mymacro` (in Nightly builds, run with -Z macro-backtrace for more info)

Playground.

Whic is quite strange because this lint is about correctness and it's incorrect even if it originates in macros, while stylistic issues, while important, are not really something I can prevent inside the macros I call.

2 Likes

Is this worth opening a bugticket for this?

Yes, the behavior seems weird to me. The PR that introduced the lint doesn’t mention / discuss the in_macro thing either AFAICT, so please feel free to open an issue.

It also seems like the clippy lint (that was uplifted into this rustc lint) was fired for macros (haven't checked it though, just judged by the code).

Alright, Linter warning not fired in macro code · Issue #94694 · rust-lang/rust · GitHub

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.