mirror of
https://github.com/salsa-rs/salsa.git
synced 2024-10-23 20:59:51 +00:00
Made proc-macros panic less
Replaced the panics in query_group with syn errors for better user feedback and experience
This commit is contained in:
parent
380c4c1dc8
commit
78b32d69da
1 changed files with 66 additions and 17 deletions
|
@ -6,7 +6,8 @@ use proc_macro::TokenStream;
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, parse_quote, Attribute, FnArg, Ident, ItemTrait, ReturnType, TraitItem, Type,
|
parse_macro_input, parse_quote, spanned::Spanned, Attribute, Error, FnArg, Ident, ItemTrait,
|
||||||
|
ReturnType, TraitItem, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Implementation for `[salsa::query_group]` decorator.
|
/// Implementation for `[salsa::query_group]` decorator.
|
||||||
|
@ -16,9 +17,15 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
|
||||||
// println!("args: {:#?}", args);
|
// println!("args: {:#?}", args);
|
||||||
// println!("input: {:#?}", input);
|
// println!("input: {:#?}", input);
|
||||||
|
|
||||||
|
let input_span = input.span();
|
||||||
let (trait_attrs, salsa_attrs) = filter_attrs(input.attrs);
|
let (trait_attrs, salsa_attrs) = filter_attrs(input.attrs);
|
||||||
if !salsa_attrs.is_empty() {
|
if !salsa_attrs.is_empty() {
|
||||||
panic!("unsupported attributes: {:?}", salsa_attrs);
|
return Error::new(
|
||||||
|
input_span,
|
||||||
|
format!("unsupported attributes: {:?}", salsa_attrs),
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
let trait_vis = input.vis;
|
let trait_vis = input.vis;
|
||||||
|
@ -43,7 +50,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
|
||||||
|
|
||||||
// Extract attributes.
|
// Extract attributes.
|
||||||
let (attrs, salsa_attrs) = filter_attrs(method.attrs);
|
let (attrs, salsa_attrs) = filter_attrs(method.attrs);
|
||||||
for SalsaAttr { name, tts } in salsa_attrs {
|
for SalsaAttr { name, tts, span } in salsa_attrs {
|
||||||
match name.as_str() {
|
match name.as_str() {
|
||||||
"memoized" => {
|
"memoized" => {
|
||||||
storage = QueryStorage::Memoized;
|
storage = QueryStorage::Memoized;
|
||||||
|
@ -74,26 +81,47 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
|
||||||
storage = QueryStorage::Transparent;
|
storage = QueryStorage::Transparent;
|
||||||
num_storages += 1;
|
num_storages += 1;
|
||||||
}
|
}
|
||||||
_ => panic!("unknown salsa attribute `{}`", name),
|
_ => {
|
||||||
|
return Error::new(span, format!("unknown salsa attribute `{}`", name))
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check attribute combinations.
|
// Check attribute combinations.
|
||||||
if num_storages > 1 {
|
if num_storages > 1 {
|
||||||
panic!("multiple storage attributes specified");
|
return Error::new(method.sig.span(), "multiple storage attributes specified")
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
}
|
}
|
||||||
if invoke.is_some() && storage == QueryStorage::Input {
|
match &invoke {
|
||||||
panic!("#[salsa::invoke] cannot be set on #[salsa::input] queries");
|
Some(invoke) if storage == QueryStorage::Input => {
|
||||||
|
return Error::new(
|
||||||
|
invoke.span(),
|
||||||
|
"#[salsa::invoke] cannot be set on #[salsa::input] queries",
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract keys.
|
// Extract keys.
|
||||||
let mut iter = method.sig.inputs.iter();
|
let mut iter = method.sig.inputs.iter();
|
||||||
match iter.next() {
|
match iter.next() {
|
||||||
Some(FnArg::Receiver(sr)) if sr.mutability.is_none() => (),
|
Some(FnArg::Receiver(sr)) if sr.mutability.is_none() => (),
|
||||||
_ => panic!(
|
_ => {
|
||||||
"first argument of query `{}` must be `&self`",
|
return Error::new(
|
||||||
method.sig.ident
|
method.sig.span(),
|
||||||
),
|
format!(
|
||||||
|
"first argument of query `{}` must be `&self`",
|
||||||
|
method.sig.ident,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let mut keys: Vec<Type> = vec![];
|
let mut keys: Vec<Type> = vec![];
|
||||||
for arg in iter {
|
for arg in iter {
|
||||||
|
@ -101,17 +129,34 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
|
||||||
FnArg::Typed(ref arg) => {
|
FnArg::Typed(ref arg) => {
|
||||||
keys.push((*arg.ty).clone());
|
keys.push((*arg.ty).clone());
|
||||||
}
|
}
|
||||||
ref a => panic!("unsupported argument `{:?}` of `{}`", a, method.sig.ident),
|
ref arg => {
|
||||||
|
return Error::new(
|
||||||
|
arg.span(),
|
||||||
|
format!(
|
||||||
|
"unsupported argument `{:?}` of `{}`",
|
||||||
|
arg, method.sig.ident,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract value.
|
// Extract value.
|
||||||
let value = match method.sig.output {
|
let value = match method.sig.output {
|
||||||
ReturnType::Type(_, ref ty) => ty.as_ref().clone(),
|
ReturnType::Type(_, ref ty) => ty.as_ref().clone(),
|
||||||
ref r => panic!(
|
ref ret => {
|
||||||
"unsupported return type `{:?}` of `{}`",
|
return Error::new(
|
||||||
r, method.sig.ident
|
ret.span(),
|
||||||
),
|
format!(
|
||||||
|
"unsupported return type `{:?}` of `{}`",
|
||||||
|
ret, method.sig.ident
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.to_compile_error()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// For `#[salsa::interned]` keys, we create a "lookup key" automatically.
|
// For `#[salsa::interned]` keys, we create a "lookup key" automatically.
|
||||||
|
@ -580,6 +625,7 @@ pub(crate) fn query_group(args: TokenStream, input: TokenStream) -> TokenStream
|
||||||
struct SalsaAttr {
|
struct SalsaAttr {
|
||||||
name: String,
|
name: String,
|
||||||
tts: TokenStream,
|
tts: TokenStream,
|
||||||
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for SalsaAttr {
|
impl std::fmt::Debug for SalsaAttr {
|
||||||
|
@ -590,14 +636,17 @@ impl std::fmt::Debug for SalsaAttr {
|
||||||
|
|
||||||
impl TryFrom<syn::Attribute> for SalsaAttr {
|
impl TryFrom<syn::Attribute> for SalsaAttr {
|
||||||
type Error = syn::Attribute;
|
type Error = syn::Attribute;
|
||||||
|
|
||||||
fn try_from(attr: syn::Attribute) -> Result<SalsaAttr, syn::Attribute> {
|
fn try_from(attr: syn::Attribute) -> Result<SalsaAttr, syn::Attribute> {
|
||||||
if is_not_salsa_attr_path(&attr.path) {
|
if is_not_salsa_attr_path(&attr.path) {
|
||||||
return Err(attr);
|
return Err(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let span = attr.span();
|
||||||
let name = attr.path.segments[1].ident.to_string();
|
let name = attr.path.segments[1].ident.to_string();
|
||||||
let tts = attr.tokens.into();
|
let tts = attr.tokens.into();
|
||||||
Ok(SalsaAttr { name, tts })
|
|
||||||
|
Ok(SalsaAttr { name, tts, span })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue