From 4ebe6e0a7e1c373b760f00dac22cc18aacd358b5 Mon Sep 17 00:00:00 2001 From: Johannes Cornelis Draaijer Date: Wed, 3 Sep 2025 13:53:50 +0200 Subject: [PATCH] rtic-macros: forward attributes applied to app module Instead of ignoring additional attributes applied to the app module, we can forward them to the generated code. --- rtic-macros/src/codegen.rs | 2 ++ rtic-macros/src/syntax/ast.rs | 5 ++++- rtic-macros/src/syntax/parse.rs | 9 ++++++++- rtic-macros/src/syntax/parse/app.rs | 1 + rtic/ui/inner_attribute.rs | 21 +++++++++++++++++++++ rtic/ui/inner_attribute.stderr | 11 +++++++++++ rtic/ui/outer_attribute.rs | 20 ++++++++++++++++++++ rtic/ui/outer_attribute.stderr | 11 +++++++++++ 8 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 rtic/ui/inner_attribute.rs create mode 100644 rtic/ui/inner_attribute.stderr create mode 100644 rtic/ui/outer_attribute.rs create mode 100644 rtic/ui/outer_attribute.stderr diff --git a/rtic-macros/src/codegen.rs b/rtic-macros/src/codegen.rs index 060db6d389d..e0cd033834e 100644 --- a/rtic-macros/src/codegen.rs +++ b/rtic-macros/src/codegen.rs @@ -44,12 +44,14 @@ pub fn app(app: &App, analysis: &Analysis) -> TokenStream2 { let user_code = &app.user_code; let name = &app.name; let device = &app.args.device; + let attribute_metas = &app.attribute_metas; let rt_err = util::rt_err_ident(); let async_limit = bindings::async_prio_limit(app, analysis); quote!( /// The RTIC application module + #(#[#attribute_metas])* pub mod #name { /// Always include the device crate which contains the vector table use #device as #rt_err; diff --git a/rtic-macros/src/syntax/ast.rs b/rtic-macros/src/syntax/ast.rs index ad73a895cf4..44c1385d24f 100644 --- a/rtic-macros/src/syntax/ast.rs +++ b/rtic-macros/src/syntax/ast.rs @@ -1,6 +1,6 @@ //! Abstract Syntax Tree -use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, PatType, Path, Stmt, Type}; +use syn::{Attribute, Expr, Ident, Item, ItemUse, Meta, Pat, PatType, Path, Stmt, Type}; use crate::syntax::{backend::BackendArgs, Map}; @@ -11,6 +11,9 @@ pub struct App { /// The arguments to the `#[app]` attribute pub args: AppArgs, + /// All attributes applied to the `#[app]` module (meta only) + pub attribute_metas: Vec, + /// The name of the `const` item on which the `#[app]` attribute has been placed pub name: Ident, diff --git a/rtic-macros/src/syntax/parse.rs b/rtic-macros/src/syntax/parse.rs index aae8a503ed8..ea7ff29409a 100644 --- a/rtic-macros/src/syntax/parse.rs +++ b/rtic-macros/src/syntax/parse.rs @@ -11,7 +11,7 @@ use syn::{ braced, parse::{self, Parse, ParseStream, Parser}, token::Brace, - Ident, Item, LitInt, Token, + Attribute, Ident, Item, LitInt, Meta, Token, }; use crate::syntax::{ @@ -28,6 +28,7 @@ pub fn app(args: TokenStream2, input: TokenStream2) -> parse::Result { } pub(crate) struct Input { + pub attribute_metas: Vec, _mod_token: Token![mod], pub ident: Ident, _brace_token: Brace, @@ -48,12 +49,18 @@ impl Parse for Input { let content; + let mut attributes = input.call(Attribute::parse_outer)?; let _mod_token = input.parse()?; let ident = input.parse()?; let _brace_token = braced!(content in input); + let inner_attributes = content.call(Attribute::parse_inner)?; let items = content.call(parse_items)?; + attributes.extend(inner_attributes); + let attribute_metas = attributes.into_iter().map(|a| a.meta).collect(); + Ok(Input { + attribute_metas, _mod_token, ident, _brace_token, diff --git a/rtic-macros/src/syntax/parse/app.rs b/rtic-macros/src/syntax/parse/app.rs index 469bcb880c2..50ef12b9fee 100644 --- a/rtic-macros/src/syntax/parse/app.rs +++ b/rtic-macros/src/syntax/parse/app.rs @@ -531,6 +531,7 @@ impl App { } Ok(App { + attribute_metas: input.attribute_metas, args, name: input.ident, init, diff --git a/rtic/ui/inner_attribute.rs b/rtic/ui/inner_attribute.rs new file mode 100644 index 00000000000..86b7f804d7f --- /dev/null +++ b/rtic/ui/inner_attribute.rs @@ -0,0 +1,21 @@ +#![no_main] +#![deny(unfulfilled_lint_expectations)] + +#[rtic::app(device = lm3s6965)] +mod app { + #![expect(while_true)] + + #[shared] + struct Shared { + #[unsafe(link_section = ".custom_section")] + foo: (), + } + + #[local] + struct Local {} + + #[init] + fn init(_cx: init::Context) -> (Shared, Local) { + (Shared { foo: () }, Local {}) + } +} diff --git a/rtic/ui/inner_attribute.stderr b/rtic/ui/inner_attribute.stderr new file mode 100644 index 00000000000..68cb571616d --- /dev/null +++ b/rtic/ui/inner_attribute.stderr @@ -0,0 +1,11 @@ +error: this lint expectation is unfulfilled + --> ui/inner_attribute.rs:6:15 + | +6 | #![expect(while_true)] + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> ui/inner_attribute.rs:2:9 + | +2 | #![deny(unfulfilled_lint_expectations)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/rtic/ui/outer_attribute.rs b/rtic/ui/outer_attribute.rs new file mode 100644 index 00000000000..f1fbe72976d --- /dev/null +++ b/rtic/ui/outer_attribute.rs @@ -0,0 +1,20 @@ +#![no_main] +#![deny(unfulfilled_lint_expectations)] + +#[rtic::app(device = lm3s6965)] +#[expect(while_true)] +mod app { + #[shared] + struct Shared { + #[unsafe(link_section = ".custom_section")] + foo: (), + } + + #[local] + struct Local {} + + #[init] + fn init(_cx: init::Context) -> (Shared, Local) { + (Shared { foo: () }, Local {}) + } +} diff --git a/rtic/ui/outer_attribute.stderr b/rtic/ui/outer_attribute.stderr new file mode 100644 index 00000000000..1bce6e745a4 --- /dev/null +++ b/rtic/ui/outer_attribute.stderr @@ -0,0 +1,11 @@ +error: this lint expectation is unfulfilled + --> ui/outer_attribute.rs:5:10 + | +5 | #[expect(while_true)] + | ^^^^^^^^^^ + | +note: the lint level is defined here + --> ui/outer_attribute.rs:2:9 + | +2 | #![deny(unfulfilled_lint_expectations)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^