diff --git a/rtic-macros/CHANGELOG.md b/rtic-macros/CHANGELOG.md index 6a1fc54049..daee6c3864 100644 --- a/rtic-macros/CHANGELOG.md +++ b/rtic-macros/CHANGELOG.md @@ -11,6 +11,10 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top! - Unstable ESP32-C3 support. +### Changed + +- Upgraded from syn 1.x to syn 2.x + ## [v2.0.1] - 2023-07-25 ### Added diff --git a/rtic-macros/Cargo.toml b/rtic-macros/Cargo.toml index e88d1bc927..5203774082 100644 --- a/rtic-macros/Cargo.toml +++ b/rtic-macros/Cargo.toml @@ -46,7 +46,7 @@ indexmap = "2.0.0" proc-macro2 = "1.0.49" proc-macro-error = "1.0.4" quote = "1.0.23" -syn = { version = "1.0.107", features = ["extra-traits", "full"] } +syn = { version = "2.0.48", features = ["extra-traits", "full"] } [dev-dependencies] trybuild = "1.0.73" diff --git a/rtic-macros/src/codegen/local_resources.rs b/rtic-macros/src/codegen/local_resources.rs index e6d15533b8..c73ad56d6e 100644 --- a/rtic-macros/src/codegen/local_resources.rs +++ b/rtic-macros/src/codegen/local_resources.rs @@ -19,7 +19,10 @@ pub fn codegen(app: &App, _analysis: &Analysis) -> TokenStream2 { // late resources in `util::link_section_uninit` // unless user specifies custom link section - let section = if attrs.iter().any(|attr| attr.path.is_ident("link_section")) { + let section = if attrs + .iter() + .any(|attr| attr.path().is_ident("link_section")) + { None } else { Some(util::link_section_uninit()) diff --git a/rtic-macros/src/codegen/shared_resources.rs b/rtic-macros/src/codegen/shared_resources.rs index 686c2809cb..d89f0c24e7 100644 --- a/rtic-macros/src/codegen/shared_resources.rs +++ b/rtic-macros/src/codegen/shared_resources.rs @@ -19,7 +19,10 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 { // late resources in `util::link_section_uninit` // unless user specifies custom link section - let section = if attrs.iter().any(|attr| attr.path.is_ident("link_section")) { + let section = if attrs + .iter() + .any(|attr| attr.path().is_ident("link_section")) + { None } else { Some(util::link_section_uninit()) diff --git a/rtic-macros/src/syntax/parse.rs b/rtic-macros/src/syntax/parse.rs index 823bd82ea9..aae8a503ed 100644 --- a/rtic-macros/src/syntax/parse.rs +++ b/rtic-macros/src/syntax/parse.rs @@ -8,7 +8,7 @@ mod util; use proc_macro2::TokenStream as TokenStream2; use syn::{ - braced, parenthesized, + braced, parse::{self, Parse, ParseStream, Parser}, token::Brace, Ident, Item, LitInt, Token, @@ -70,15 +70,12 @@ fn init_args(tokens: TokenStream2) -> parse::Result { let mut local_resources = None; - let content; - parenthesized!(content in input); - - if !content.is_empty() { + if !input.is_empty() { loop { // Parse identifier name - let ident: Ident = content.parse()?; + let ident: Ident = input.parse()?; // Handle equal sign - let _: Token![=] = content.parse()?; + let _: Token![=] = input.parse()?; match &*ident.to_string() { "local" => { @@ -89,18 +86,18 @@ fn init_args(tokens: TokenStream2) -> parse::Result { )); } - local_resources = Some(util::parse_local_resources(&content)?); + local_resources = Some(util::parse_local_resources(input)?); } _ => { return Err(parse::Error::new(ident.span(), "unexpected argument")); } } - if content.is_empty() { + if input.is_empty() { break; } // Handle comma: , - let _: Token![,] = content.parse()?; + let _: Token![,] = input.parse()?; } } @@ -131,14 +128,12 @@ fn idle_args(tokens: TokenStream2) -> parse::Result { let mut shared_resources = None; let mut local_resources = None; - let content; - parenthesized!(content in input); - if !content.is_empty() { + if !input.is_empty() { loop { // Parse identifier name - let ident: Ident = content.parse()?; + let ident: Ident = input.parse()?; // Handle equal sign - let _: Token![=] = content.parse()?; + let _: Token![=] = input.parse()?; match &*ident.to_string() { "shared" => { @@ -149,7 +144,7 @@ fn idle_args(tokens: TokenStream2) -> parse::Result { )); } - shared_resources = Some(util::parse_shared_resources(&content)?); + shared_resources = Some(util::parse_shared_resources(input)?); } "local" => { @@ -160,19 +155,19 @@ fn idle_args(tokens: TokenStream2) -> parse::Result { )); } - local_resources = Some(util::parse_local_resources(&content)?); + local_resources = Some(util::parse_local_resources(input)?); } _ => { return Err(parse::Error::new(ident.span(), "unexpected argument")); } } - if content.is_empty() { + if input.is_empty() { break; } // Handle comma: , - let _: Token![,] = content.parse()?; + let _: Token![,] = input.parse()?; } } @@ -196,19 +191,17 @@ fn task_args(tokens: TokenStream2) -> parse::Result { @@ -220,7 +213,7 @@ fn task_args(tokens: TokenStream2) -> parse::Result parse::Result parse::Result { @@ -274,7 +267,7 @@ fn task_args(tokens: TokenStream2) -> parse::Result { @@ -282,12 +275,12 @@ fn task_args(tokens: TokenStream2) -> parse::Result { check_binding(&args.binds)?; check_ident(&item.sig.ident)?; @@ -369,8 +375,9 @@ impl App { .iter() .position(|attr| util::attr_eq(attr, "init")) { - let args = InitArgs::parse(item.attrs.remove(pos).tokens)?; - + let args = InitArgs::parse( + item.attrs.remove(pos).parse_args().unwrap_or_default(), + )?; // If an init function already exists, error if init.is_some() { return Err(parse::Error::new( @@ -387,7 +394,9 @@ impl App { .iter() .position(|attr| util::attr_eq(attr, "idle")) { - let args = IdleArgs::parse(item.attrs.remove(pos).tokens)?; + let args = IdleArgs::parse( + item.attrs.remove(pos).parse_args().unwrap_or_default(), + )?; // If an idle function already exists, error if idle.is_some() { @@ -421,7 +430,9 @@ impl App { )); } - match syntax_parse::task_args(item.attrs.remove(pos).tokens)? { + match syntax_parse::task_args( + item.attrs.remove(pos).parse_args().unwrap_or_default(), + )? { Either::Left(args) => { check_binding(&args.binds)?; check_ident(&item.sig.ident)?; diff --git a/rtic-macros/src/syntax/parse/init.rs b/rtic-macros/src/syntax/parse/init.rs index 59d00937ce..e96d5f978e 100644 --- a/rtic-macros/src/syntax/parse/init.rs +++ b/rtic-macros/src/syntax/parse/init.rs @@ -1,5 +1,4 @@ -use proc_macro2::TokenStream as TokenStream2; - +use crate::syntax::TokenStream2; use syn::{parse, ForeignItemFn, ItemFn, Stmt}; use crate::syntax::{ diff --git a/rtic-macros/src/syntax/parse/util.rs b/rtic-macros/src/syntax/parse/util.rs index 5a5e0c0ef2..44d9759f4d 100644 --- a/rtic-macros/src/syntax/parse/util.rs +++ b/rtic-macros/src/syntax/parse/util.rs @@ -3,8 +3,8 @@ use syn::{ parse::{self, ParseStream}, punctuated::Punctuated, spanned::Spanned, - Abi, AttrStyle, Attribute, Expr, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, Path, - PathArguments, ReturnType, Token, Type, Visibility, + Abi, AttrStyle, Attribute, Expr, ExprPath, FnArg, ForeignItemFn, Ident, ItemFn, Pat, PatType, + Path, PathArguments, ReturnType, Token, Type, Visibility, }; use crate::syntax::{ @@ -20,8 +20,8 @@ pub fn abi_is_rust(abi: &Abi) -> bool { } 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(); + 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 } } @@ -143,91 +143,140 @@ fn extract_resource_name_ident(path: Path) -> parse::Result { } pub fn parse_local_resources(content: ParseStream<'_>) -> parse::Result { - let inner; - bracketed!(inner in content); + let input; + bracketed!(input in content); let mut resources = Map::new(); - for e in inner.call(Punctuated::::parse_terminated)? { - let err = Err(parse::Error::new( - e.span(), - "identifier appears more than once in list", - )); + let error_msg_no_local_resources = + "malformed, expected 'local = [EXPRPATH: TYPE = EXPR]', or 'local = [EXPRPATH, ...]'"; - 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", - )); - } + loop { + if input.is_empty() { + break; + } + // Type ascription is de-RFCd from Rust in + // https://github.com/rust-lang/rfcs/pull/3307 + // Manually pull out the tokens - let ident = extract_resource_name_ident(path.path)?; - // let (cfgs, attrs) = extract_cfgs(path.attrs); + // Two acceptable variants: + // + // Task local and declared (initialized in place) + // local = [EXPRPATH: TYPE = EXPR, ...] + // ~~~~~~~~~~~~~~~~~~~~~~ + // or + // Task local but not initialized + // local = [EXPRPATH, ...], + // ~~~~~~~~~ - (ident, TaskLocal::External) - } + // Common: grab first identifier EXPRPATH + // local = [EXPRPATH: TYPE = EXPR, ...] + // ~~~~~~~~ + let exprpath: ExprPath = input.parse()?; - // 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); + let name = extract_resource_name_ident(exprpath.path)?; - (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'", - )) - } + // Extract attributes + let ExprPath { attrs, .. } = exprpath; + let (cfgs, attrs) = { + let FilterAttrs { cfgs, attrs, .. } = filter_attributes(attrs); + (cfgs, attrs) }; + let local; + + // Declared requries type ascription + if input.peek(Token![:]) { + // Handle colon + let _: Token![:] = input.parse()?; + + // Extract the type + let ty: Box = input.parse()?; + + if input.peek(Token![=]) { + // Handle equal sign + let _: Token![=] = input.parse()?; + } else { + return Err(parse::Error::new( + name.span(), + "malformed, expected 'IDENT: TYPE = EXPR'", + )); + } + + // Grab the final expression right of equal + let expr: Box = input.parse()?; + + // We got a trailing colon, remove it + if input.peek(Token![,]) { + let _: Token![,] = input.parse()?; + } + + // 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", + )) + } + }; + + local = TaskLocal::Declared(Local { + attrs, + cfgs, + ty, + expr, + }); + } else if input.peek(Token![=]) { + // Missing type ascription is not valid + return Err(parse::Error::new(name.span(), "malformed, expected a type")); + } else if input.peek(Token![,]) { + // Attributes not supported on non-initialized local resources! + + if !attrs.is_empty() { + return Err(parse::Error::new( + name.span(), + "attributes are not supported here", + )); + } + + // Remove comma + let _: Token![,] = input.parse()?; + + // Expected when multiple local resources + local = TaskLocal::External; + } else if input.is_empty() { + // There was only one single local resource + // Task local but not initialized + // local = [EXPRPATH], + // ~~~~~~~~ + local = TaskLocal::External; + } else { + // Specifying local without any resources is invalid + return Err(parse::Error::new(name.span(), error_msg_no_local_resources)); + }; + + if resources.contains_key(&name) { + return Err(parse::Error::new( + name.span(), + "resource appears more than once in list", + )); + } + resources.insert(name, local); } + if resources.is_empty() { + return Err(parse::Error::new( + input.span(), + error_msg_no_local_resources, + )); + } + Ok(resources) } diff --git a/rtic-macros/ui/local-malformed-1.stderr b/rtic-macros/ui/local-malformed-1.stderr index d15c324bc3..6e68f0bc3a 100644 --- a/rtic-macros/ui/local-malformed-1.stderr +++ b/rtic-macros/ui/local-malformed-1.stderr @@ -1,4 +1,4 @@ -error: unexpected end of input, expected one of: `for`, parentheses, `fn`, `unsafe`, `extern`, identifier, `::`, `<`, square brackets, `*`, `&`, `!`, `impl`, `_`, lifetime +error: unexpected end of input, expected one of: `for`, parentheses, `fn`, `unsafe`, `extern`, identifier, `::`, `<`, `dyn`, square brackets, `*`, `&`, `!`, `impl`, `_`, lifetime --> ui/local-malformed-1.rs:11:23 | 11 | #[task(local = [a:])] diff --git a/rtic-macros/ui/local-malformed-2.stderr b/rtic-macros/ui/local-malformed-2.stderr index 0b448f01ba..ceb04058fe 100644 --- a/rtic-macros/ui/local-malformed-2.stderr +++ b/rtic-macros/ui/local-malformed-2.stderr @@ -2,4 +2,4 @@ error: malformed, expected 'IDENT: TYPE = EXPR' --> ui/local-malformed-2.rs:11:21 | 11 | #[task(local = [a: u32])] - | ^^^^^^ + | ^ diff --git a/rtic-macros/ui/local-malformed-3.stderr b/rtic-macros/ui/local-malformed-3.stderr index 61af4f38d5..3636bf100f 100644 --- a/rtic-macros/ui/local-malformed-3.stderr +++ b/rtic-macros/ui/local-malformed-3.stderr @@ -1,4 +1,4 @@ -error: unexpected end of input, expected expression +error: unexpected end of input, expected an expression --> ui/local-malformed-3.rs:11:29 | 11 | #[task(local = [a: u32 =])] diff --git a/rtic-macros/ui/local-shared-attribute.stderr b/rtic-macros/ui/local-shared-attribute.stderr index a8130e8890..f18f5b7a8a 100644 --- a/rtic-macros/ui/local-shared-attribute.stderr +++ b/rtic-macros/ui/local-shared-attribute.stderr @@ -1,6 +1,5 @@ error: attributes are not supported here - --> ui/local-shared-attribute.rs:17:9 + --> ui/local-shared-attribute.rs:18:9 | -17 | / #[test] -18 | | b, // Error - | |_________^ +18 | b, // Error + | ^