|
@@ -3,19 +3,19 @@ use quote::{quote, ToTokens};
|
|
|
use syn::{parse_macro_input, punctuated::Punctuated, Data, DeriveInput, Fields, Generics, Ident};
|
|
|
|
|
|
#[derive(Clone, Default)]
|
|
|
-struct RustSBIImp<'a> {
|
|
|
- fence: Option<&'a Ident>,
|
|
|
- hsm: Option<&'a Ident>,
|
|
|
- ipi: Option<&'a Ident>,
|
|
|
- reset: Option<&'a Ident>,
|
|
|
- timer: Option<&'a Ident>,
|
|
|
- pmu: Option<&'a Ident>,
|
|
|
- console: Option<&'a Ident>,
|
|
|
- susp: Option<&'a Ident>,
|
|
|
- cppc: Option<&'a Ident>,
|
|
|
- nacl: Option<&'a Ident>,
|
|
|
- sta: Option<&'a Ident>,
|
|
|
- env_info: Option<&'a Ident>,
|
|
|
+struct RustSBIImp {
|
|
|
+ fence: Option<Ident>,
|
|
|
+ hsm: Option<Ident>,
|
|
|
+ ipi: Option<Ident>,
|
|
|
+ reset: Option<Ident>,
|
|
|
+ timer: Option<Ident>,
|
|
|
+ pmu: Option<Ident>,
|
|
|
+ console: Option<Ident>,
|
|
|
+ susp: Option<Ident>,
|
|
|
+ cppc: Option<Ident>,
|
|
|
+ nacl: Option<Ident>,
|
|
|
+ sta: Option<Ident>,
|
|
|
+ env_info: Option<Ident>,
|
|
|
}
|
|
|
|
|
|
/// This macro should be used in `rustsbi` crate as `rustsbi::RustSBI`.
|
|
@@ -34,18 +34,67 @@ pub fn derive_rustsbi(input: TokenStream) -> TokenStream {
|
|
|
};
|
|
|
let mut imp = RustSBIImp::default();
|
|
|
|
|
|
+ let mut replace_sbi_extension_ident = |extension_name: &str, ident: Ident| match extension_name
|
|
|
+ {
|
|
|
+ "rfnc" | "fence" => (true, imp.fence.replace(ident)),
|
|
|
+ "hsm" => (true, imp.hsm.replace(ident)),
|
|
|
+ "spi" | "ipi" => (true, imp.ipi.replace(ident)),
|
|
|
+ "srst" | "reset" => (true, imp.reset.replace(ident)),
|
|
|
+ "time" | "timer" => (true, imp.timer.replace(ident)),
|
|
|
+ "pmu" => (true, imp.pmu.replace(ident)),
|
|
|
+ "dbcn" | "console" => (true, imp.console.replace(ident)),
|
|
|
+ "susp" => (true, imp.susp.replace(ident)),
|
|
|
+ "cppc" => (true, imp.cppc.replace(ident)),
|
|
|
+ "nacl" => (true, imp.nacl.replace(ident)),
|
|
|
+ "sta" => (true, imp.sta.replace(ident)),
|
|
|
+ "info" | "env_info" => (true, imp.env_info.replace(ident)),
|
|
|
+ _ => (false, None),
|
|
|
+ };
|
|
|
let mut ans = TokenStream::new();
|
|
|
+ let check_already_exists =
|
|
|
+ |field: &syn::Field, extension_name: &str, origin: Option<Ident>, ans: &mut TokenStream| {
|
|
|
+ if let Some(_origin) = origin {
|
|
|
+ // TODO: provide more detailed proc macro error hinting that previous
|
|
|
+ // definition of this extension resides in `origin` once RFC 1566
|
|
|
+ // (Procedural Macro Diagnostics) is stablized.
|
|
|
+ // Link: https://github.com/rust-lang/rust/issues/54140
|
|
|
+ let error = syn::Error::new_spanned(
|
|
|
+ &field,
|
|
|
+ format!(
|
|
|
+ "more than one field defined SBI extension '{}'. \
|
|
|
+ At most one fields should define the same SBI extension; consider using \
|
|
|
+ #[rustsbi(skip)] to ignore fields that shouldn't be treated as an extension.",
|
|
|
+ extension_name
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ ans.extend(TokenStream::from(error.to_compile_error()));
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
for field in &fields {
|
|
|
- let mut skipped = false;
|
|
|
+ let mut field_already_parsed = false;
|
|
|
for attr in &field.attrs {
|
|
|
if !attr.path().is_ident("rustsbi") {
|
|
|
continue;
|
|
|
}
|
|
|
let parsed = attr.parse_nested_meta(|meta| {
|
|
|
+ let mut current_meta_accepted = false;
|
|
|
if meta.path.is_ident("skip") {
|
|
|
- // skip this field in RustSBI
|
|
|
- skipped = true;
|
|
|
+ // accept meta but do nothing, effectively skip this field in RustSBI
|
|
|
+ current_meta_accepted = true;
|
|
|
+ } else if let (Some(meta_path_ident), Some(field_ident)) =
|
|
|
+ (meta.path.get_ident(), &field.ident)
|
|
|
+ {
|
|
|
+ let extension_name = &meta_path_ident.to_string();
|
|
|
+ let (replaced, origin) =
|
|
|
+ replace_sbi_extension_ident(extension_name, field_ident.clone());
|
|
|
+ if replaced {
|
|
|
+ check_already_exists(field, extension_name, origin, &mut ans);
|
|
|
+ current_meta_accepted = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if current_meta_accepted {
|
|
|
+ field_already_parsed = true;
|
|
|
Ok(())
|
|
|
} else {
|
|
|
let path = meta.path.to_token_stream().to_string().replace(' ', "");
|
|
@@ -56,38 +105,15 @@ pub fn derive_rustsbi(input: TokenStream) -> TokenStream {
|
|
|
ans.extend(TokenStream::from(err.to_compile_error()));
|
|
|
}
|
|
|
}
|
|
|
- if skipped {
|
|
|
+ // Already parsed by inner attribute.
|
|
|
+ // Could be either skipped using #[rustsbi(skip)], or renamed using #[rustsbi(some_extension)]
|
|
|
+ if field_already_parsed {
|
|
|
continue;
|
|
|
}
|
|
|
- if let Some(name) = &field.ident {
|
|
|
- let origin = match name.to_string().as_str() {
|
|
|
- "rfnc" | "fence" => imp.fence.replace(name),
|
|
|
- "hsm" => imp.hsm.replace(name),
|
|
|
- "spi" | "ipi" => imp.ipi.replace(name),
|
|
|
- "srst" | "reset" => imp.reset.replace(name),
|
|
|
- "time" | "timer" => imp.timer.replace(name),
|
|
|
- "pmu" => imp.pmu.replace(name),
|
|
|
- "dbcn" | "console" => imp.console.replace(name),
|
|
|
- "susp" => imp.susp.replace(name),
|
|
|
- "cppc" => imp.cppc.replace(name),
|
|
|
- "nacl" => imp.nacl.replace(name),
|
|
|
- "sta" => imp.sta.replace(name),
|
|
|
- "info" | "env_info" => imp.env_info.replace(name),
|
|
|
- _ => continue,
|
|
|
- };
|
|
|
- if let Some(_origin) = origin {
|
|
|
- // TODO: provide more detailed proc macro error hinting that previous
|
|
|
- // definition of this extension resides in `origin` once RFC 1566
|
|
|
- // (Procedural Macro Diagnostics) is stablized.
|
|
|
- // Link: https://github.com/rust-lang/rust/issues/54140
|
|
|
- let error = syn::Error::new_spanned(
|
|
|
- &field,
|
|
|
- format!("more than one field defined SBI extension '{}'. \
|
|
|
- At most one fields should define the same SBI extension; consider using \
|
|
|
- #[rustsbi(skip)] to ignore fields that shouldn't be treated as an extension.", name),
|
|
|
- );
|
|
|
- ans.extend(TokenStream::from(error.to_compile_error()));
|
|
|
- }
|
|
|
+ if let Some(field_ident) = &field.ident {
|
|
|
+ let (_replaced, origin) =
|
|
|
+ replace_sbi_extension_ident(field_ident.to_string().as_str(), field_ident.clone());
|
|
|
+ check_already_exists(field, &field_ident.to_string(), origin, &mut ans);
|
|
|
}
|
|
|
}
|
|
|
|