Why does the compiler interpret macros twice?

Hello,

I would like to know why the Rust compiler interprets macros twice.

I created a test project to try to understand why, here are the important pieces of code.

Workplace
|
|--- test_macro
|
|--- app_test


crate : test_macro

mprintln allows debugging what happens during the execution of the macro code and writes the output to a file (since println! doesn’t work in macros). I also avoided using other macros in mprintln to prevent any interference (recursive macros).


fn mprintln(str: impl Into<String>) {
    use std::{fs::OpenOptions, io::Write};
    let file_path = "./output_macro.txt";

    let mut file = OpenOptions::new()
        .create(true)
        .append(true)
        .open(file_path)
        .expect("Error");
    let timestamp = SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .unwrap()
        .as_nanos();

    let input_str = str.into();

    let mut line = String::new();
    line.push_str("(ts: ");
    line.push_str(&timestamp.to_string()); 
    line.push_str(") | ");
    line.push_str(&input_str);
    line.push('\n');

    file.write_all(line.as_bytes())
        .expect("Erreur");
}

#[proc_macro_attribute]
#[allow(non_snake_case)]
pub fn RestController(args: TokenStream, input: TokenStream) -> TokenStream {
    mprintln("a");

    let result = quote!();
    return TokenStream::from(result);

}

crate : app_test

use test_macro::RestController;

pub struct TestController {}

#[RestController]
impl TestController {}

I have this result:

(ts: 1741988183078171739) | a
(ts: 1741988183078342025) | a

Normally, I should have only one line when I press Ctrl+S on the file, but I get two lines, as if the compiler interprets the macro twice each time.

I don’t know if this is normal behavior; if it is, why does it do that and consume more resources?

Thank you in advance for your help. :pray:

Do you have rust-analyzer running? Could be that both rustc and rust-analyzer ran the prof macro independently.

1 Like

I didn’t start a build. If it’s rust-analyzer doing it, why does it compile the same code twice? Isn’t there a way to optimize this?

I expect the macro is being run once by RA itself while analyzing the code, and once by cargo check started by check-on-save. Neither of these can reuse each other's results because there is no cache of specifically macro execution that is specifically a public feature of rustc that RA can use.

You can disable either or both of check-on-save and RA's proc macro support, but you will then get less functionality.

I think you will also see the macro being run while you edit the code that uses the macro (without saving).

1 Like

What you say seems very logical, thank you very much for your clarifications. :pray: