diff options
author | 2023-01-23 20:05:47 +0100 | |
---|---|---|
committer | 2023-03-01 00:33:31 +0100 | |
commit | 306aa47170fd59369b7a184924e287dc3706d64d (patch) | |
tree | 75a331a63a4021f078e330bf2ce4edb1228e2ecf /macros/src/syntax/parse | |
parent | b8b881f446a226d6f3c4a7db7c9174590b47dbf6 (diff) | |
download | rtic-306aa47170fd59369b7a184924e287dc3706d64d.tar.gz rtic-306aa47170fd59369b7a184924e287dc3706d64d.tar.zst rtic-306aa47170fd59369b7a184924e287dc3706d64d.zip |
Add rtic-timer (timerqueue + monotonic) and rtic-monotonics (systick-monotonic)
Diffstat (limited to 'macros/src/syntax/parse')
-rw-r--r-- | macros/src/syntax/parse/app.rs | 480 | ||||
-rw-r--r-- | macros/src/syntax/parse/hardware_task.rs | 76 | ||||
-rw-r--r-- | macros/src/syntax/parse/idle.rs | 42 | ||||
-rw-r--r-- | macros/src/syntax/parse/init.rs | 51 | ||||
-rw-r--r-- | macros/src/syntax/parse/resource.rs | 55 | ||||
-rw-r--r-- | macros/src/syntax/parse/software_task.rs | 76 | ||||
-rw-r--r-- | macros/src/syntax/parse/util.rs | 338 |
7 files changed, 0 insertions, 1118 deletions
diff --git a/macros/src/syntax/parse/app.rs b/macros/src/syntax/parse/app.rs deleted file mode 100644 index e797f75e..00000000 --- a/macros/src/syntax/parse/app.rs +++ /dev/null @@ -1,480 +0,0 @@ -use std::collections::HashSet; - -// use indexmap::map::Entry; -use proc_macro2::TokenStream as TokenStream2; -use syn::{ - parse::{self, ParseStream, Parser}, - spanned::Spanned, - Expr, ExprArray, Fields, ForeignItem, Ident, Item, LitBool, Path, Token, Visibility, -}; - -use super::Input; -use crate::syntax::{ - ast::{ - App, AppArgs, Dispatcher, Dispatchers, HardwareTask, Idle, IdleArgs, Init, InitArgs, - LocalResource, SharedResource, SoftwareTask, - }, - parse::{self as syntax_parse, util}, - Either, Map, Set, -}; - -impl AppArgs { - pub(crate) fn parse(tokens: TokenStream2) -> parse::Result<Self> { - (|input: ParseStream<'_>| -> parse::Result<Self> { - let mut custom = Set::new(); - let mut device = None; - let mut peripherals = true; - let mut dispatchers = Dispatchers::new(); - - loop { - if input.is_empty() { - break; - } - - // #ident = .. - let ident: Ident = input.parse()?; - let _eq_token: Token![=] = input.parse()?; - - if custom.contains(&ident) { - return Err(parse::Error::new( - ident.span(), - "argument appears more than once", - )); - } - - custom.insert(ident.clone()); - - let ks = ident.to_string(); - - match &*ks { - "device" => { - if let Ok(p) = input.parse::<Path>() { - device = Some(p); - } else { - return Err(parse::Error::new( - ident.span(), - "unexpected argument value; this should be a path", - )); - } - } - - "peripherals" => { - if let Ok(p) = input.parse::<LitBool>() { - peripherals = p.value; - } else { - return Err(parse::Error::new( - ident.span(), - "unexpected argument value; this should be a boolean", - )); - } - } - - "dispatchers" => { - if let Ok(p) = input.parse::<ExprArray>() { - for e in p.elems { - match e { - Expr::Path(ep) => { - let path = ep.path; - let ident = if path.leading_colon.is_some() - || path.segments.len() != 1 - { - return Err(parse::Error::new( - path.span(), - "interrupt must be an identifier, not a path", - )); - } else { - path.segments[0].ident.clone() - }; - let span = ident.span(); - if dispatchers.contains_key(&ident) { - return Err(parse::Error::new( - span, - "this extern interrupt is listed more than once", - )); - } else { - dispatchers - .insert(ident, Dispatcher { attrs: ep.attrs }); - } - } - _ => { - return Err(parse::Error::new( - e.span(), - "interrupt must be an identifier", - )); - } - } - } - } else { - return Err(parse::Error::new( - ident.span(), - // increasing the length of the error message will break rustfmt - "unexpected argument value; expected an array", - )); - } - } - _ => { - return Err(parse::Error::new(ident.span(), "unexpected argument")); - } - } - - if input.is_empty() { - break; - } - - // , - let _: Token![,] = input.parse()?; - } - - let device = if let Some(device) = device { - device - } else { - return Err(parse::Error::new(input.span(), "missing `device = ...`")); - }; - - Ok(AppArgs { - device, - peripherals, - dispatchers, - }) - }) - .parse2(tokens) - } -} - -impl App { - pub(crate) fn parse(args: AppArgs, input: Input) -> parse::Result<Self> { - let mut init = None; - let mut idle = None; - - let mut shared_resources_ident = None; - let mut shared_resources = Map::new(); - let mut local_resources_ident = None; - let mut local_resources = Map::new(); - let mut hardware_tasks = Map::new(); - let mut software_tasks = Map::new(); - let mut user_imports = vec![]; - let mut user_code = vec![]; - - let mut seen_idents = HashSet::<Ident>::new(); - let mut bindings = HashSet::<Ident>::new(); - - let mut check_binding = |ident: &Ident| { - if bindings.contains(ident) { - return Err(parse::Error::new( - ident.span(), - "this interrupt is already bound", - )); - } else { - bindings.insert(ident.clone()); - } - - Ok(()) - }; - - let mut check_ident = |ident: &Ident| { - if seen_idents.contains(ident) { - return Err(parse::Error::new( - ident.span(), - "this identifier has already been used", - )); - } else { - seen_idents.insert(ident.clone()); - } - - Ok(()) - }; - - for mut item in input.items { - match item { - Item::Fn(mut item) => { - let span = item.sig.ident.span(); - if let Some(pos) = item - .attrs - .iter() - .position(|attr| util::attr_eq(attr, "init")) - { - let args = InitArgs::parse(item.attrs.remove(pos).tokens)?; - - // If an init function already exists, error - if init.is_some() { - return Err(parse::Error::new( - span, - "`#[init]` function must appear at most once", - )); - } - - check_ident(&item.sig.ident)?; - - init = Some(Init::parse(args, item)?); - } else if let Some(pos) = item - .attrs - .iter() - .position(|attr| util::attr_eq(attr, "idle")) - { - let args = IdleArgs::parse(item.attrs.remove(pos).tokens)?; - - // If an idle function already exists, error - if idle.is_some() { - return Err(parse::Error::new( - span, - "`#[idle]` function must appear at most once", - )); - } - - check_ident(&item.sig.ident)?; - - idle = Some(Idle::parse(args, item)?); - } else if let Some(pos) = item - .attrs - .iter() - .position(|attr| util::attr_eq(attr, "task")) - { - if hardware_tasks.contains_key(&item.sig.ident) - || software_tasks.contains_key(&item.sig.ident) - { - return Err(parse::Error::new( - span, - "this task is defined multiple times", - )); - } - - match syntax_parse::task_args(item.attrs.remove(pos).tokens)? { - Either::Left(args) => { - check_binding(&args.binds)?; - check_ident(&item.sig.ident)?; - - hardware_tasks.insert( - item.sig.ident.clone(), - HardwareTask::parse(args, item)?, - ); - } - - Either::Right(args) => { - check_ident(&item.sig.ident)?; - - software_tasks.insert( - item.sig.ident.clone(), - SoftwareTask::parse(args, item)?, - ); - } - } - } else { - // Forward normal functions - user_code.push(Item::Fn(item.clone())); - } - } - - Item::Struct(ref mut struct_item) => { - // Match structures with the attribute #[shared], name of structure is not - // important - if let Some(_pos) = struct_item - .attrs - .iter() - .position(|attr| util::attr_eq(attr, "shared")) - { - let span = struct_item.ident.span(); - - shared_resources_ident = Some(struct_item.ident.clone()); - - if !shared_resources.is_empty() { - return Err(parse::Error::new( - span, - "`#[shared]` struct must appear at most once", - )); - } - - if struct_item.vis != Visibility::Inherited { - return Err(parse::Error::new( - struct_item.span(), - "this item must have inherited / private visibility", - )); - } - - if let Fields::Named(fields) = &mut struct_item.fields { - for field in &mut fields.named { - let ident = field.ident.as_ref().expect("UNREACHABLE"); - - if shared_resources.contains_key(ident) { - return Err(parse::Error::new( - ident.span(), - "this resource is listed more than once", - )); - } - - shared_resources.insert( - ident.clone(), - SharedResource::parse(field, ident.span())?, - ); - } - } else { - return Err(parse::Error::new( - struct_item.span(), - "this `struct` must have named fields", - )); - } - } else if let Some(_pos) = struct_item - .attrs - .iter() - .position(|attr| util::attr_eq(attr, "local")) - { - let span = struct_item.ident.span(); - - local_resources_ident = Some(struct_item.ident.clone()); - - if !local_resources.is_empty() { - return Err(parse::Error::new( - span, - "`#[local]` struct must appear at most once", - )); - } - - if struct_item.vis != Visibility::Inherited { - return Err(parse::Error::new( - struct_item.span(), - "this item must have inherited / private visibility", - )); - } - - if let Fields::Named(fields) = &mut struct_item.fields { - for field in &mut fields.named { - let ident = field.ident.as_ref().expect("UNREACHABLE"); - - if local_resources.contains_key(ident) { - return Err(parse::Error::new( - ident.span(), - "this resource is listed more than once", - )); - } - - local_resources.insert( - ident.clone(), - LocalResource::parse(field, ident.span())?, - ); - } - } else { - return Err(parse::Error::new( - struct_item.span(), - "this `struct` must have named fields", - )); - } - } else { - // Structure without the #[resources] attribute should just be passed along - user_code.push(item.clone()); - } - } - - Item::ForeignMod(mod_) => { - if !util::abi_is_rust(&mod_.abi) { - return Err(parse::Error::new( - mod_.abi.extern_token.span(), - "this `extern` block must use the \"Rust\" ABI", - )); - } - - for item in mod_.items { - if let ForeignItem::Fn(mut item) = item { - let span = item.sig.ident.span(); - if let Some(pos) = item - .attrs - .iter() - .position(|attr| util::attr_eq(attr, "task")) - { - if hardware_tasks.contains_key(&item.sig.ident) - || software_tasks.contains_key(&item.sig.ident) - { - return Err(parse::Error::new( - span, - "this task is defined multiple times", - )); - } - - if item.attrs.len() != 1 { - return Err(parse::Error::new( - span, - "`extern` task required `#[task(..)]` attribute", - )); - } - - match syntax_parse::task_args(item.attrs.remove(pos).tokens)? { - Either::Left(args) => { - check_binding(&args.binds)?; - check_ident(&item.sig.ident)?; - - hardware_tasks.insert( - item.sig.ident.clone(), - HardwareTask::parse_foreign(args, item)?, - ); - } - - Either::Right(args) => { - check_ident(&item.sig.ident)?; - - software_tasks.insert( - item.sig.ident.clone(), - SoftwareTask::parse_foreign(args, item)?, - ); - } - } - } else { - return Err(parse::Error::new( - span, - "`extern` task required `#[task(..)]` attribute", - )); - } - } else { - return Err(parse::Error::new( - item.span(), - "this item must live outside the `#[app]` module", - )); - } - } - } - Item::Use(itemuse_) => { - // Store the user provided use-statements - user_imports.push(itemuse_.clone()); - } - _ => { - // Anything else within the module should not make any difference - user_code.push(item.clone()); - } - } - } - - let shared_resources_ident = - shared_resources_ident.expect("No `#[shared]` resource struct defined"); - let local_resources_ident = - local_resources_ident.expect("No `#[local]` resource struct defined"); - let init = init.expect("No `#[init]` function defined"); - - if shared_resources_ident != init.user_shared_struct { - return Err(parse::Error::new( - init.user_shared_struct.span(), - format!( - "This name and the one defined on `#[shared]` are not the same. Should this be `{shared_resources_ident}`?" - ), - )); - } - - if local_resources_ident != init.user_local_struct { - return Err(parse::Error::new( - init.user_local_struct.span(), - format!( - "This name and the one defined on `#[local]` are not the same. Should this be `{local_resources_ident}`?" - ), - )); - } - - Ok(App { - args, - name: input.ident, - init, - idle, - shared_resources, - local_resources, - user_imports, - user_code, - hardware_tasks, - software_tasks, - }) - } -} diff --git a/macros/src/syntax/parse/hardware_task.rs b/macros/src/syntax/parse/hardware_task.rs deleted file mode 100644 index 7f6dfbe4..00000000 --- a/macros/src/syntax/parse/hardware_task.rs +++ /dev/null @@ -1,76 +0,0 @@ -use syn::{parse, ForeignItemFn, ItemFn, Stmt}; - -use crate::syntax::parse::util::FilterAttrs; -use crate::syntax::{ - ast::{HardwareTask, HardwareTaskArgs}, - parse::util, -}; - -impl HardwareTask { - pub(crate) fn parse(args: HardwareTaskArgs, item: ItemFn) -> parse::Result<Self> { - let span = item.sig.ident.span(); - let valid_signature = util::check_fn_signature(&item, false) - && item.sig.inputs.len() == 1 - && util::type_is_unit(&item.sig.output); - - let name = item.sig.ident.to_string(); - - if valid_signature { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); - - return Ok(HardwareTask { - args, - cfgs, - attrs, - context, - stmts: item.block.stmts, - is_extern: false, - }); - } - } - } - - Err(parse::Error::new( - span, - format!("this task handler must have type signature `fn({name}::Context)`"), - )) - } -} - -impl HardwareTask { - pub(crate) fn parse_foreign( - args: HardwareTaskArgs, - item: ForeignItemFn, - ) -> parse::Result<Self> { - let span = item.sig.ident.span(); - let valid_signature = util::check_foreign_fn_signature(&item, false) - && item.sig.inputs.len() == 1 - && util::type_is_unit(&item.sig.output); - - let name = item.sig.ident.to_string(); - - if valid_signature { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); - - return Ok(HardwareTask { - args, - cfgs, - attrs, - context, - stmts: Vec::<Stmt>::new(), - is_extern: true, - }); - } - } - } - - Err(parse::Error::new( - span, - format!("this task handler must have type signature `fn({name}::Context)`"), - )) - } -} diff --git a/macros/src/syntax/parse/idle.rs b/macros/src/syntax/parse/idle.rs deleted file mode 100644 index 124c1366..00000000 --- a/macros/src/syntax/parse/idle.rs +++ /dev/null @@ -1,42 +0,0 @@ -use proc_macro2::TokenStream as TokenStream2; -use syn::{parse, ItemFn}; - -use crate::syntax::{ - ast::{Idle, IdleArgs}, - parse::util, -}; - -impl IdleArgs { - pub(crate) fn parse(tokens: TokenStream2) -> parse::Result<Self> { - crate::syntax::parse::idle_args(tokens) - } -} - -impl Idle { - pub(crate) fn parse(args: IdleArgs, item: ItemFn) -> parse::Result<Self> { - let valid_signature = util::check_fn_signature(&item, false) - && item.sig.inputs.len() == 1 - && util::type_is_bottom(&item.sig.output); - - let name = item.sig.ident.to_string(); - - if valid_signature { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - return Ok(Idle { - args, - attrs: item.attrs, - context, - name: item.sig.ident, - stmts: item.block.stmts, - }); - } - } - } - - Err(parse::Error::new( - item.sig.ident.span(), - format!("this `#[idle]` function must have signature `fn({name}::Context) -> !`"), - )) - } -} diff --git a/macros/src/syntax/parse/init.rs b/macros/src/syntax/parse/init.rs deleted file mode 100644 index 0aea20bd..00000000 --- a/macros/src/syntax/parse/init.rs +++ /dev/null @@ -1,51 +0,0 @@ -use proc_macro2::TokenStream as TokenStream2; - -use syn::{parse, ItemFn}; - -use crate::syntax::{ - ast::{Init, InitArgs}, - parse::{self as syntax_parse, util}, -}; - -impl InitArgs { - pub(crate) fn parse(tokens: TokenStream2) -> parse::Result<Self> { - syntax_parse::init_args(tokens) - } -} - -impl Init { - pub(crate) fn parse(args: InitArgs, item: ItemFn) -> parse::Result<Self> { - let valid_signature = util::check_fn_signature(&item, false) && item.sig.inputs.len() == 1; - - let span = item.sig.ident.span(); - - let name = item.sig.ident.to_string(); - - if valid_signature { - if let Ok((user_shared_struct, user_local_struct)) = - util::type_is_init_return(&item.sig.output) - { - if let Some((context, Ok(rest))) = util::parse_inputs(item.sig.inputs, &name) { - if rest.is_empty() { - return Ok(Init { - args, - attrs: item.attrs, - context, - name: item.sig.ident, - stmts: item.block.stmts, - user_shared_struct, - user_local_struct, - }); - } - } - } - } - - Err(parse::Error::new( - span, - format!( - "the `#[init]` function must have signature `fn({name}::Context) -> (Shared resources struct, Local resources struct)`" - ), - )) - } -} diff --git a/macros/src/syntax/parse/resource.rs b/macros/src/syntax/parse/resource.rs deleted file mode 100644 index ff100576..00000000 --- a/macros/src/syntax/parse/resource.rs +++ /dev/null @@ -1,55 +0,0 @@ -use proc_macro2::Span; -use syn::{parse, Field, Visibility}; - -use crate::syntax::parse::util::FilterAttrs; -use crate::syntax::{ - ast::{LocalResource, SharedResource, SharedResourceProperties}, - parse::util, -}; - -impl SharedResource { - pub(crate) fn parse(item: &Field, span: Span) -> parse::Result<Self> { - if item.vis != Visibility::Inherited { - return Err(parse::Error::new( - span, - "this field must have inherited / private visibility", - )); - } - - let FilterAttrs { - cfgs, - mut attrs, - docs, - } = util::filter_attributes(item.attrs.clone()); - - let lock_free = util::extract_lock_free(&mut attrs)?; - - Ok(SharedResource { - cfgs, - attrs, - docs, - ty: Box::new(item.ty.clone()), - properties: SharedResourceProperties { lock_free }, - }) - } -} - -impl LocalResource { - pub(crate) fn parse(item: &Field, span: Span) -> parse::Result<Self> { - if item.vis != Visibility::Inherited { - return Err(parse::Error::new( - span, - "this field must have inherited / private visibility", - )); - } - - let FilterAttrs { cfgs, attrs, docs } = util::filter_attributes(item.attrs.clone()); - - Ok(LocalResource { - cfgs, - attrs, - docs, - ty: Box::new(item.ty.clone()), - }) - } -} diff --git a/macros/src/syntax/parse/software_task.rs b/macros/src/syntax/parse/software_task.rs deleted file mode 100644 index 769aa653..00000000 --- a/macros/src/syntax/parse/software_task.rs +++ /dev/null @@ -1,76 +0,0 @@ -use syn::{parse, ForeignItemFn, ItemFn, Stmt}; - -use crate::syntax::parse::util::FilterAttrs; -use crate::syntax::{ - ast::{SoftwareTask, SoftwareTaskArgs}, - parse::util, -}; - -impl SoftwareTask { - pub(crate) fn parse(args: SoftwareTaskArgs, item: ItemFn) -> parse::Result<Self> { - let valid_signature = util::check_fn_signature(&item, true) - && util::type_is_unit(&item.sig.output) - && item.sig.asyncness.is_some(); - - let span = item.sig.ident.span(); - - let name = item.sig.ident.to_string(); - - if valid_signature { - if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); - - return Ok(SoftwareTask { - args, - attrs, - cfgs, - context, - inputs, - stmts: item.block.stmts, - is_extern: false, - }); - } - } - - Err(parse::Error::new( - span, - format!("this task handler must have type signature `async fn({name}::Context, ..)`"), - )) - } -} - -impl SoftwareTask { - pub(crate) fn parse_foreign( - args: SoftwareTaskArgs, - item: ForeignItemFn, - ) -> parse::Result<Self> { - let valid_signature = util::check_foreign_fn_signature(&item, true) - && util::type_is_unit(&item.sig.output) - && item.sig.asyncness.is_some(); - - let span = item.sig.ident.span(); - - let name = item.sig.ident.to_string(); - - if valid_signature { - if let Some((context, Ok(inputs))) = util::parse_inputs(item.sig.inputs, &name) { - let FilterAttrs { cfgs, attrs, .. } = util::filter_attributes(item.attrs); - - return Ok(SoftwareTask { - args, - attrs, - cfgs, - context, - inputs, - stmts: Vec::<Stmt>::new(), - is_extern: true, - }); - } - } - - Err(parse::Error::new( - span, - format!("this task handler must have type signature `async fn({name}::Context, ..)`"), - )) - } -} diff --git a/macros/src/syntax/parse/util.rs b/macros/src/syntax/parse/util.rs deleted file mode 100644 index 5a5e0c0e..00000000 --- a/macros/src/syntax/parse/util.rs +++ /dev/null @@ -1,338 +0,0 @@ -use syn::{ - bracketed, - parse::{self, ParseStream}, - punctuated::Punctuated, - spanned::Spanned, - Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path, - PathArguments, ReturnType, Token, Type, Visibility, -}; - -use crate::syntax::{ - ast::{Access, Local, LocalResources, SharedResources, TaskLocal}, - Map, -}; - -pub fn abi_is_rust(abi: &Abi) -> bool { - match &abi.name { - None => true, - Some(s) => s.value() == "Rust", - } -} - -pub fn attr_eq(attr: &Attribute, name: &str) -> bool { - attr.style == AttrStyle::Outer && attr.path.segments.len() == 1 && { - let segment = attr.path.segments.first().unwrap(); - segment.arguments == PathArguments::None && *segment.ident.to_string() == *name - } -} - -/// checks that a function signature -/// -/// - has no bounds (like where clauses) -/// - is not `async` -/// - is not `const` -/// - is not `unsafe` -/// - is not generic (has no type parameters) -/// - is not variadic -/// - uses the Rust ABI (and not e.g. "C") -pub fn check_fn_signature(item: &ItemFn, allow_async: bool) -> bool { - item.vis == Visibility::Inherited - && item.sig.constness.is_none() - && (item.sig.asyncness.is_none() || allow_async) - && item.sig.abi.is_none() - && item.sig.unsafety.is_none() - && item.sig.generics.params.is_empty() - && item.sig.generics.where_clause.is_none() - && item.sig.variadic.is_none() -} - -#[allow(dead_code)] -pub fn check_foreign_fn_signature(item: &ForeignItemFn, allow_async: bool) -> bool { - item.vis == Visibility::Inherited - && item.sig.constness.is_none() - && (item.sig.asyncness.is_none() || allow_async) - && item.sig.abi.is_none() - && item.sig.unsafety.is_none() - && item.sig.generics.params.is_empty() - && item.sig.generics.where_clause.is_none() - && item.sig.variadic.is_none() -} - -pub struct FilterAttrs { - pub cfgs: Vec<Attribute>, - pub docs: Vec<Attribute>, - pub attrs: Vec<Attribute>, -} - -pub fn filter_attributes(input_attrs: Vec<Attribute>) -> FilterAttrs { - let mut cfgs = vec![]; - let mut docs = vec![]; - let mut attrs = vec![]; - - for attr in input_attrs { - if attr_eq(&attr, "cfg") { - cfgs.push(attr); - } else if attr_eq(&attr, "doc") { - docs.push(attr); - } else { - attrs.push(attr); - } - } - - FilterAttrs { cfgs, docs, attrs } -} - -pub fn extract_lock_free(attrs: &mut Vec<Attribute>) -> parse::Result<bool> { - if let Some(pos) = attrs.iter().position(|attr| attr_eq(attr, "lock_free")) { - attrs.remove(pos); - Ok(true) - } else { - Ok(false) - } -} - -pub fn parse_shared_resources(content: ParseStream<'_>) -> parse::Result<SharedResources> { - let inner; - bracketed!(inner in content); - - let mut resources = Map::new(); - for e in inner.call(Punctuated::<Expr, Token![,]>::parse_terminated)? { - let err = Err(parse::Error::new( - e.span(), - "identifier appears more than once in list", - )); - let (access, path) = match e { - Expr::Path(e) => (Access::Exclusive, e.path), - - Expr::Reference(ref r) if r.mutability.is_none() => match &*r.expr { - Expr::Path(e) => (Access::Shared, e.path.clone()), - - _ => return err, - }, - - _ => return err, - }; - - let ident = extract_resource_name_ident(path)?; - - if resources.contains_key(&ident) { - return Err(parse::Error::new( - ident.span(), - "resource appears more than once in list", - )); - } - - resources.insert(ident, access); - } - - Ok(resources) -} - -fn extract_resource_name_ident(path: Path) -> parse::Result<Ident> { - if path.leading_colon.is_some() - || path.segments.len() != 1 - || path.segments[0].arguments != PathArguments::None - { - Err(parse::Error::new( - path.span(), - "resource must be an identifier, not a path", - )) - } else { - Ok(path.segments[0].ident.clone()) - } -} - -pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result<LocalResources> { - let inner; - bracketed!(inner in content); - - let mut resources = Map::new(); - - for e in inner.call(Punctuated::<Expr, Token![,]>::parse_terminated)? { - let err = Err(parse::Error::new( - e.span(), - "identifier appears more than once in list", - )); - - let (name, local) = match e { - // local = [IDENT], - Expr::Path(path) => { - if !path.attrs.is_empty() { - return Err(parse::Error::new( - path.span(), - "attributes are not supported here", - )); - } - - let ident = extract_resource_name_ident(path.path)?; - // let (cfgs, attrs) = extract_cfgs(path.attrs); - - (ident, TaskLocal::External) - } - - // local = [IDENT: TYPE = EXPR] - Expr::Assign(e) => { - let (name, ty, cfgs, attrs) = match *e.left { - Expr::Type(t) => { - // Extract name and attributes - let (name, cfgs, attrs) = match *t.expr { - Expr::Path(path) => { - let name = extract_resource_name_ident(path.path)?; - let FilterAttrs { cfgs, attrs, .. } = filter_attributes(path.attrs); - - (name, cfgs, attrs) - } - _ => return err, - }; - - let ty = t.ty; - - // Error check - match &*ty { - Type::Array(_) => {} - Type::Path(_) => {} - Type::Ptr(_) => {} - Type::Tuple(_) => {} - _ => return Err(parse::Error::new( - ty.span(), - "unsupported type, must be an array, tuple, pointer or type path", - )), - }; - - (name, ty, cfgs, attrs) - } - e => return Err(parse::Error::new(e.span(), "malformed, expected a type")), - }; - - let expr = e.right; // Expr - - ( - name, - TaskLocal::Declared(Local { - attrs, - cfgs, - ty, - expr, - }), - ) - } - - expr => { - return Err(parse::Error::new( - expr.span(), - "malformed, expected 'IDENT: TYPE = EXPR'", - )) - } - }; - - resources.insert(name, local); - } - - Ok(resources) -} - -type ParseInputResult = Option<(Box<Pat>, Result<Vec<PatType>, FnArg>)>; - -pub fn parse_inputs(inputs: Punctuated<FnArg, Token![,]>, name: &str) -> ParseInputResult { - let mut inputs = inputs.into_iter(); - - match inputs.next() { - Some(FnArg::Typed(first)) => { - if type_is_path(&first.ty, &[name, "Context"]) { - let rest = inputs - .map(|arg| match arg { - FnArg::Typed(arg) => Ok(arg), - _ => Err(arg), - }) - .collect::<Result<Vec<_>, _>>(); - - Some((first.pat, rest)) - } else { - None - } - } - - _ => None, - } -} - -pub fn type_is_bottom(ty: &ReturnType) -> bool { - if let ReturnType::Type(_, ty) = ty { - matches!(**ty, Type::Never(_)) - } else { - false - } -} - -fn extract_init_resource_name_ident(ty: Type) -> Result<Ident, ()> { - match ty { - Type::Path(path) => { - let path = path.path; - - if path.leading_colon.is_some() - || path.segments.len() != 1 - || path.segments[0].arguments != PathArguments::None - { - Err(()) - } else { - Ok(path.segments[0].ident.clone()) - } - } - _ => Err(()), - } -} - -/// Checks Init's return type, return the user provided types for analysis -pub fn type_is_init_return(ty: &ReturnType) -> Result<(Ident, Ident), ()> { - match ty { - ReturnType::Default => Err(()), - - ReturnType::Type(_, ty) => match &**ty { - Type::Tuple(t) => { - // return should be: - // fn -> (User's #[shared] struct, User's #[local] struct) - // - // We check the length and the last one here, analysis checks that the user - // provided structs are correct. - if t.elems.len() == 2 { - return Ok(( - extract_init_resource_name_ident(t.elems[0].clone())?, - extract_init_resource_name_ident(t.elems[1].clone())?, - )); - } - - Err(()) - } - - _ => Err(()), - }, - } -} - -pub fn type_is_path(ty: &Type, segments: &[&str]) -> bool { - match ty { - Type::Path(tpath) if tpath.qself.is_none() => { - tpath.path.segments.len() == segments.len() - && tpath - .path - .segments - .iter() - .zip(segments) - .all(|(lhs, rhs)| lhs.ident == **rhs) - } - - _ => false, - } -} - -pub fn type_is_unit(ty: &ReturnType) -> bool { - if let ReturnType::Type(_, ty) = ty { - if let Type::Tuple(ref tuple) = **ty { - tuple.elems.is_empty() - } else { - false - } - } else { - true - } -} |