rtic/macros/src/check.rs

123 lines
4 KiB
Rust
Raw Normal View History

2018-11-03 17:02:41 +01:00
use std::{collections::HashSet, iter};
2018-11-03 17:02:41 +01:00
use proc_macro2::Span;
use syn::parse;
2018-12-16 19:13:22 +01:00
use crate::syntax::App;
2018-11-03 17:02:41 +01:00
pub fn app(app: &App) -> parse::Result<()> {
// Check that all referenced resources have been declared
for res in app
.idle
.as_ref()
2018-12-16 19:13:22 +01:00
.map(|idle| -> Box<dyn Iterator<Item = _>> { Box::new(idle.args.resources.iter()) })
2018-11-03 17:02:41 +01:00
.unwrap_or_else(|| Box::new(iter::empty()))
.chain(&app.init.args.resources)
.chain(app.exceptions.values().flat_map(|e| &e.args.resources))
.chain(app.interrupts.values().flat_map(|i| &i.args.resources))
.chain(app.tasks.values().flat_map(|t| &t.args.resources))
{
if !app.resources.contains_key(res) {
return Err(parse::Error::new(
res.span(),
"this resource has NOT been declared",
));
}
}
2018-11-03 17:02:41 +01:00
// Check that late resources have not been assigned to `init`
for res in &app.init.args.resources {
if app.resources.get(res).unwrap().expr.is_none() {
return Err(parse::Error::new(
res.span(),
"late resources can NOT be assigned to `init`",
));
}
}
if app.resources.iter().any(|(_, res)| res.expr.is_none()) {
// Check that `init` returns `LateResources` if there's any declared late resource
if !app.init.returns_late_resources {
return Err(parse::Error::new(
app.init.span,
"late resources have been specified so `init` must return `init::LateResources`",
));
}
} else if app.init.returns_late_resources {
// If there are no late resources the signature should be `fn(init::Context)`
if app.init.returns_late_resources {
return Err(parse::Error::new(
app.init.span,
"`init` signature must be `fn(init::Context)` if there are no late resources",
));
}
2018-11-03 17:02:41 +01:00
}
2018-11-03 17:02:41 +01:00
// Check that all referenced tasks have been declared
for task in app
.idle
.as_ref()
2018-12-16 19:13:22 +01:00
.map(|idle| -> Box<dyn Iterator<Item = _>> {
2018-11-03 17:02:41 +01:00
Box::new(idle.args.schedule.iter().chain(&idle.args.spawn))
})
.unwrap_or_else(|| Box::new(iter::empty()))
.chain(&app.init.args.schedule)
.chain(&app.init.args.spawn)
.chain(
app.exceptions
.values()
.flat_map(|e| e.args.schedule.iter().chain(&e.args.spawn)),
)
.chain(
app.interrupts
.values()
.flat_map(|i| i.args.schedule.iter().chain(&i.args.spawn)),
)
.chain(
app.tasks
.values()
.flat_map(|t| t.args.schedule.iter().chain(&t.args.spawn)),
)
{
2018-11-03 17:02:41 +01:00
if !app.tasks.contains_key(task) {
return Err(parse::Error::new(
task.span(),
"this task has NOT been declared",
));
}
}
2018-11-03 17:02:41 +01:00
// Check that there are enough free interrupts to dispatch all tasks
let ndispatchers = app
.tasks
.values()
.map(|t| t.args.priority)
.collect::<HashSet<_>>()
.len();
if ndispatchers > app.free_interrupts.len() {
return Err(parse::Error::new(
Span::call_site(),
&*format!(
"{} free interrupt{} (`extern {{ .. }}`) {} required to dispatch all soft tasks",
ndispatchers,
if ndispatchers > 1 { "s" } else { "" },
if ndispatchers > 1 { "are" } else { "is" },
),
));
}
2018-11-03 17:02:41 +01:00
// Check that free interrupts are not being used
for (handler, interrupt) in &app.interrupts {
let name = interrupt.args.binds(handler);
2019-02-23 21:54:56 +01:00
if app.free_interrupts.contains_key(name) {
2018-11-03 17:02:41 +01:00
return Err(parse::Error::new(
2019-02-23 21:54:56 +01:00
name.span(),
2018-11-03 17:02:41 +01:00
"free interrupts (`extern { .. }`) can't be used as interrupt handlers",
));
}
2018-11-03 17:02:41 +01:00
}
2018-11-03 17:02:41 +01:00
Ok(())
2017-07-04 18:26:11 +02:00
}