Unnamed repository; edit this file 'description' to name the repository.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! Proc macro ABI

use libloading::Library;
use proc_macro_api::{ProcMacroKind, RustCInfo};

use crate::{dylib::LoadProcMacroDylibError, tt};

pub(crate) struct ProcMacros {
    exported_macros: Vec<proc_macro::bridge::client::ProcMacro>,
}

impl From<proc_macro::bridge::PanicMessage> for crate::PanicMessage {
    fn from(p: proc_macro::bridge::PanicMessage) -> Self {
        Self { message: p.as_str().map(|s| s.to_string()) }
    }
}

impl ProcMacros {
    /// Load a new ABI.
    ///
    /// # Arguments
    ///
    /// *`lib` - The dynamic library containing the macro implementations
    /// *`symbol_name` - The symbol name the macros can be found attributes
    /// *`info` - RustCInfo about the compiler that was used to compile the
    ///           macro crate. This is the information we use to figure out
    ///           which ABI to return
    pub fn from_lib(
        lib: &Library,
        symbol_name: String,
        info: RustCInfo,
    ) -> Result<ProcMacros, LoadProcMacroDylibError> {
        if info.version_string == crate::RUSTC_VERSION_STRING {
            let macros = unsafe {
                lib.get::<&&[proc_macro::bridge::client::ProcMacro]>(symbol_name.as_bytes())
            }?;

            return Ok(Self { exported_macros: macros.to_vec() });
        }

        // if we reached this point, versions didn't match. in testing, we
        // want that to panic - this could mean that the format of `rustc
        // --version` no longer matches the format of the version string
        // stored in the `.rustc` section, and we want to catch that in-tree
        // with `x.py test`
        if cfg!(test) {
            panic!(
                "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}",
                info.version_string, crate::RUSTC_VERSION_STRING
            );
        }
        Err(LoadProcMacroDylibError::AbiMismatch(info.version_string))
    }

    pub fn expand(
        &self,
        macro_name: &str,
        macro_body: &tt::Subtree,
        attributes: Option<&tt::Subtree>,
    ) -> Result<tt::Subtree, crate::PanicMessage> {
        let parsed_body = crate::server::TokenStream::with_subtree(macro_body.clone());

        let parsed_attributes = attributes.map_or(crate::server::TokenStream::new(), |attr| {
            crate::server::TokenStream::with_subtree(attr.clone())
        });

        for proc_macro in &self.exported_macros {
            match proc_macro {
                proc_macro::bridge::client::ProcMacro::CustomDerive {
                    trait_name, client, ..
                } if *trait_name == macro_name => {
                    let res = client.run(
                        &proc_macro::bridge::server::SameThread,
                        crate::server::RustAnalyzer::default(),
                        parsed_body,
                        true,
                    );
                    return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from);
                }
                proc_macro::bridge::client::ProcMacro::Bang { name, client }
                    if *name == macro_name =>
                {
                    let res = client.run(
                        &proc_macro::bridge::server::SameThread,
                        crate::server::RustAnalyzer::default(),
                        parsed_body,
                        true,
                    );
                    return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from);
                }
                proc_macro::bridge::client::ProcMacro::Attr { name, client }
                    if *name == macro_name =>
                {
                    let res = client.run(
                        &proc_macro::bridge::server::SameThread,
                        crate::server::RustAnalyzer::default(),
                        parsed_attributes,
                        parsed_body,
                        true,
                    );
                    return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from);
                }
                _ => continue,
            }
        }

        Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into())
    }

    pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
        self.exported_macros
            .iter()
            .map(|proc_macro| match proc_macro {
                proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
                    (trait_name.to_string(), ProcMacroKind::CustomDerive)
                }
                proc_macro::bridge::client::ProcMacro::Bang { name, .. } => {
                    (name.to_string(), ProcMacroKind::FuncLike)
                }
                proc_macro::bridge::client::ProcMacro::Attr { name, .. } => {
                    (name.to_string(), ProcMacroKind::Attr)
                }
            })
            .collect()
    }
}

#[test]
fn test_version_check() {
    let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path());
    let info = proc_macro_api::read_dylib_info(&path).unwrap();
    assert_eq!(info.version_string, crate::RUSTC_VERSION_STRING);
}