Fix macros breaking auto completion in IDEs

This commit is contained in:
William Venner 2022-01-22 19:36:41 +00:00
parent 92072ef93f
commit 3e708a0c0d
4 changed files with 61 additions and 45 deletions

6
Cargo.lock generated
View File

@ -135,7 +135,7 @@ dependencies = [
[[package]] [[package]]
name = "gmod" name = "gmod"
version = "12.0.0" version = "12.0.1"
dependencies = [ dependencies = [
"cfg_table 1.0.0", "cfg_table 1.0.0",
"cstr", "cstr",
@ -144,7 +144,7 @@ dependencies = [
"fn_abi", "fn_abi",
"fn_has_this", "fn_has_this",
"fn_type_alias", "fn_type_alias",
"gmod-macros 2.0.0", "gmod-macros 2.0.1",
"gmserverplugin", "gmserverplugin",
"lazy_static", "lazy_static",
"libloading", "libloading",
@ -165,7 +165,7 @@ dependencies = [
[[package]] [[package]]
name = "gmod-macros" name = "gmod-macros"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "gmod-macros" name = "gmod-macros"
version = "2.0.0" version = "2.0.1"
authors = ["William Venner <william@venner.io>"] authors = ["William Venner <william@venner.io>"]
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"

View File

@ -8,6 +8,16 @@ use proc_macro::TokenStream;
use quote::ToTokens; use quote::ToTokens;
use syn::ItemFn; use syn::ItemFn;
macro_rules! wrap_compile_error {
($input:ident, $code:expr) => {{
let orig_tokens = $input.clone();
match (|| -> Result<TokenStream, syn::Error> { $code })() {
Ok(tokens) => tokens,
Err(_) => return orig_tokens
}
}};
}
fn check_lua_function(input: &mut ItemFn) { fn check_lua_function(input: &mut ItemFn) {
assert!(input.sig.asyncness.is_none(), "Cannot be async"); assert!(input.sig.asyncness.is_none(), "Cannot be async");
assert!(input.sig.constness.is_none(), "Cannot be const"); assert!(input.sig.constness.is_none(), "Cannot be const");
@ -24,66 +34,72 @@ fn genericify_return(item_fn: &mut ItemFn) {
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream { pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(tokens as ItemFn); wrap_compile_error!(tokens, {
let mut input = syn::parse::<ItemFn>(tokens)?;
let lua_ident = format_ident!("{}", match &input.sig.inputs[0] { let lua_ident = format_ident!("{}", match &input.sig.inputs[0] {
syn::FnArg::Typed(arg) => arg.pat.to_token_stream().to_string(), syn::FnArg::Typed(arg) => arg.pat.to_token_stream().to_string(),
_ => unreachable!(), _ => unreachable!(),
}); });
// Capture the Lua state // Capture the Lua state
input.block.stmts.insert(0, syn::parse2(quote!(::gmod::lua::__set_state__internal(#lua_ident);)).unwrap()); input.block.stmts.insert(0, syn::parse2(quote!(::gmod::lua::__set_state__internal(#lua_ident);)).unwrap());
// Load lua_shared // Load lua_shared
input.block.stmts.insert(0, syn::parse2(quote!(#[allow(unused_unsafe)] unsafe { ::gmod::lua::load() })).unwrap()); input.block.stmts.insert(0, syn::parse2(quote!(#[allow(unused_unsafe)] unsafe { ::gmod::lua::load() })).unwrap());
// Make sure it's valid // Make sure it's valid
check_lua_function(&mut input); check_lua_function(&mut input);
// No mangling // No mangling
input.attrs.push(parse_quote!(#[no_mangle])); input.attrs.push(parse_quote!(#[no_mangle]));
// Make the return type nice and dynamic // Make the return type nice and dynamic
genericify_return(&mut input); genericify_return(&mut input);
input.into_token_stream().into() Ok(input.into_token_stream().into())
})
} }
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn gmod13_close(_attr: TokenStream, tokens: TokenStream) -> TokenStream { pub fn gmod13_close(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(tokens as ItemFn); wrap_compile_error!(tokens, {
let mut input = syn::parse::<ItemFn>(tokens)?;
// Make sure it's valid // Make sure it's valid
check_lua_function(&mut input); check_lua_function(&mut input);
// No mangling // No mangling
input.attrs.push(parse_quote!(#[no_mangle])); input.attrs.push(parse_quote!(#[no_mangle]));
// Shutdown gmcl thread if it's running // Shutdown gmcl thread if it's running
#[cfg(feature = "gmcl")] { #[cfg(feature = "gmcl")] {
let stmts = std::mem::take(&mut input.block.stmts); let stmts = std::mem::take(&mut input.block.stmts);
input.block.stmts = vec![syn::parse2(quote!({ input.block.stmts = vec![syn::parse2(quote!({
let ret = (|| {#(#stmts);*})(); let ret = (|| {#(#stmts);*})();
::gmod::gmcl::restore_stdout(); ::gmod::gmcl::restore_stdout();
ret ret
})).unwrap()]; })).unwrap()];
} }
// Make the return type nice and dynamic // Make the return type nice and dynamic
genericify_return(&mut input); genericify_return(&mut input);
input.into_token_stream().into() Ok(input.into_token_stream().into())
})
} }
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn lua_function(_attr: TokenStream, tokens: TokenStream) -> TokenStream { pub fn lua_function(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(tokens as ItemFn); wrap_compile_error!(tokens, {
let mut input = syn::parse::<ItemFn>(tokens)?;
// Make sure it's valid // Make sure it's valid
check_lua_function(&mut input); check_lua_function(&mut input);
// Make the return type nice and dynamic // Make the return type nice and dynamic
genericify_return(&mut input); genericify_return(&mut input);
input.into_token_stream().into() Ok(input.into_token_stream().into())
})
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "gmod" name = "gmod"
version = "12.0.0" version = "12.0.1"
authors = ["William Venner <william@venner.io>"] authors = ["William Venner <william@venner.io>"]
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
@ -16,7 +16,7 @@ server-plugin = ["gmserverplugin"]
gmcl = ["gmod-macros/gmcl"] gmcl = ["gmod-macros/gmcl"]
[dependencies] [dependencies]
gmod-macros = { version = "2.0.0", path = "../gmod-macros" } gmod-macros = { version = "2.0.1", path = "../gmod-macros" }
gmserverplugin = { version = "1", optional = true } gmserverplugin = { version = "1", optional = true }
libloading = "0" libloading = "0"