Есть ли способ получить файл и путь к модулю, где процедурный макрос присоединен во время компиляции? - PullRequest
1 голос
/ 18 марта 2020

Я ищу эквивалент file!() & module_path!() в контексте процедурного макроса.

Например, следующее не работает:

file.rs:

#[some_attribute]
const A: bool = true;

macro.rs:

#[proc_macro_attribute]
pub fn some_attribute(attr: TokenStream, input: TokenStream) -> TokenStream {
    println!("{}", file!());

    input
}

Это печатает macro.rs, что имеет смысл, но я хочу file.rs. Есть ли способ добиться этого? Существует ли аналогичный способ для module_path!()?

Требование этого должно происходить во время компиляции.

Я пытаюсь создать файл в OUT_DIR содержит постоянные значения, где атрибут добавляется вместе с модулем, и файл, в котором они находятся.

1 Ответ

1 голос
/ 18 марта 2020

Проблема здесь в том, что println!("{}", file!()); выполняется в время компиляции , а не во время выполнения. Подобно ответу, который недавно был дан здесь , вы можете отредактировать исходную функцию и вставить свой код в ее начало, которое будет выполнено в время выполнения на этот раз. Вы все еще можете использовать процедурные макросы file!() и module_path!(). Вот macro.rs с таким подходом:

#[proc_macro_attribute]
pub fn some_attribute(_attr: TokenStream, input: TokenStream) -> TokenStream {

    // prefix to be added to the function's body
    let mut prefix: TokenStream = "
        println!(\"Called from {:?} inside module path {:?}\",
            file!(), module_path!());
    ".parse().unwrap();

    // edit TokenStream
    input.into_iter().map(|tt| { 
        match tt { 
            TokenTree::Group(ref g) // match function body
                if g.delimiter() == proc_macro::Delimiter::Brace => { 

                    // add logic before function body
                    prefix.extend(g.stream()); 

                    // return new function body as TokenTree
                    TokenTree::Group(proc_macro::Group::new(
                        proc_macro::Delimiter::Brace, prefix.clone()))
            },
            other => other, // else just forward
        }
    }).collect()
} 

Вы можете использовать его следующим образом в main.rs:

use mylib::some_attribute;

#[some_attribute]
fn yo() -> () { println!("yo"); }

fn main() { yo(); }

Обратите внимание, что код добавляется перед что находится внутри тела функции. Мы могли бы вставить его в конце, но это лишило бы возможности возврата значения без точки с запятой.

РЕДАКТИРОВАТЬ: Позже понял, что OP хочет, чтобы он работал во время время компиляции .

...