Skip to content

Commit f4b0e64

Browse files
bors[bot]robamu
andauthored
Merge #544
544: Better error handling r=burrbull a=robamu Just a small attempt to make `svd2rust` print more useful information if something goes wrong in the TokenStream generation, or in general - More debug output in case something goes wrong in the TokenStream generation - Some info output in the main I like how the `svd` XML parser handles errors by passing an Error Enum with a node ID up which can be used by one central error handler to print diagnostic information. I don't think it can be done like that here, so I simply print out some locally available information (peripheral name, register name etc.) as the error is propagated Co-authored-by: Robin Mueller <robin.mueller.m@gmail.com>
2 parents cc9fd06 + d534093 commit f4b0e64

File tree

5 files changed

+112
-39
lines changed

5 files changed

+112
-39
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2222
match the cluster layout. Requires the `--const_generic` command line option.
2323
- Bumped `xtensa-lx` and add `xtensa_lx::interrupt::InterruptNumber` implementation.
2424
- Don't use a mask when the width of the mask is the same as the width of the parent register.
25+
- Improved error handling
2526

2627
## [v0.19.0] - 2021-05-26
2728

src/generate/device.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use crate::svd::Device;
22
use proc_macro2::{Ident, Span, TokenStream};
33
use quote::{quote, ToTokens};
4+
45
use std::fs::File;
56
use std::io::Write;
67

78
use crate::util::{self, Config, ToSanitizedUpperCase};
89
use crate::Target;
9-
use anyhow::Result;
10+
use anyhow::{Context, Result};
1011

1112
use crate::generate::{interrupt, peripheral};
1213

@@ -191,12 +192,27 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result<Toke
191192
continue;
192193
}
193194

194-
out.extend(peripheral::render(
195-
p,
196-
&d.peripherals,
197-
&d.default_register_properties,
198-
config,
199-
)?);
195+
match peripheral::render(p, &d.peripherals, &d.default_register_properties, config) {
196+
Ok(periph) => out.extend(periph),
197+
Err(e) => {
198+
let descrip = p.description.as_deref().unwrap_or("No description");
199+
let group_name = p.group_name.as_deref().unwrap_or("No group name");
200+
let mut context_string = format!(
201+
"Rendering error at peripheral\nName: {}\nDescription: {}\nGroup: {}",
202+
p.name, descrip, group_name
203+
);
204+
if p.derived_from.is_some() {
205+
context_string = format!(
206+
"{}\nDerived from: {}",
207+
context_string,
208+
p.derived_from.as_deref().unwrap()
209+
);
210+
}
211+
let mut e = Err(e);
212+
e = e.with_context(|| context_string);
213+
return e;
214+
}
215+
};
200216

201217
if p.registers
202218
.as_ref()

src/generate/peripheral.rs

Lines changed: 63 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use quote::{quote, ToTokens};
1212
use syn::{parse_str, Token};
1313

1414
use crate::util::{
15-
self, Config, FullName, ToSanitizedSnakeCase, ToSanitizedUpperCase, BITS_PER_BYTE,
15+
self, handle_cluster_error, handle_reg_error, Config, FullName, ToSanitizedSnakeCase,
16+
ToSanitizedUpperCase, BITS_PER_BYTE,
1617
};
1718
use anyhow::{anyhow, bail, Context, Result};
1819

@@ -194,14 +195,13 @@ pub fn render(
194195

195196
// Push all register related information into the peripheral module
196197
for reg in registers {
197-
mod_items.extend(register::render(
198-
reg,
199-
registers,
200-
p,
201-
all_peripherals,
202-
&defaults,
203-
config,
204-
)?);
198+
match register::render(reg, registers, p, all_peripherals, &defaults, config) {
199+
Ok(rendered_reg) => mod_items.extend(rendered_reg),
200+
Err(e) => {
201+
let res: Result<TokenStream> = Err(e);
202+
return handle_reg_error("Error rendering register", *reg, res);
203+
}
204+
};
205205
}
206206

207207
let description =
@@ -454,7 +454,8 @@ fn register_or_cluster_block(
454454
let mut accessors = TokenStream::new();
455455
let mut have_accessors = false;
456456

457-
let ercs_expanded = expand(ercs, defs, name, config)?;
457+
let ercs_expanded = expand(ercs, defs, name, config)
458+
.with_context(|| "Could not expand register or cluster block")?;
458459

459460
// Locate conflicting regions; we'll need to use unions to represent them.
460461
let mut regions = FieldRegions::default();
@@ -596,10 +597,30 @@ fn expand(
596597
let mut ercs_expanded = vec![];
597598

598599
for erc in ercs {
599-
ercs_expanded.extend(match &erc {
600-
RegisterCluster::Register(register) => expand_register(register, defs, name, config)?,
601-
RegisterCluster::Cluster(cluster) => expand_cluster(cluster, defs, name, config)?,
602-
});
600+
match &erc {
601+
RegisterCluster::Register(register) => {
602+
match expand_register(register, defs, name, config) {
603+
Ok(expanded_reg) => ercs_expanded.extend(expanded_reg),
604+
Err(e) => {
605+
let res = Err(e);
606+
return handle_reg_error("Error expanding register", register, res);
607+
}
608+
}
609+
}
610+
RegisterCluster::Cluster(cluster) => {
611+
match expand_cluster(cluster, defs, name, config) {
612+
Ok(expanded_cluster) => ercs_expanded.extend(expanded_cluster),
613+
Err(e) => {
614+
let res = Err(e);
615+
return handle_cluster_error(
616+
"Error expanding register cluster",
617+
cluster,
618+
res,
619+
);
620+
}
621+
}
622+
}
623+
};
603624
}
604625

605626
ercs_expanded.sort_by_key(|x| x.offset);
@@ -740,7 +761,8 @@ fn expand_register(
740761

741762
match register {
742763
Register::Single(info) => register_expanded.push(RegisterBlockField {
743-
field: convert_svd_register(register, name, config.ignore_groups)?,
764+
field: convert_svd_register(register, name, config.ignore_groups)
765+
.with_context(|| "syn error occured")?,
744766
description: info.description.clone().unwrap_or_default(),
745767
offset: info.address_offset,
746768
size: register_size,
@@ -815,14 +837,17 @@ fn cluster_block(
815837
// Generate definition for each of the registers.
816838
let registers = util::only_registers(&c.children);
817839
for reg in &registers {
818-
mod_items.extend(register::render(
819-
reg,
820-
&registers,
821-
p,
822-
all_peripherals,
823-
&defaults,
824-
config,
825-
)?);
840+
match register::render(reg, &registers, p, all_peripherals, &defaults, config) {
841+
Ok(rendered_reg) => mod_items.extend(rendered_reg),
842+
Err(e) => {
843+
let res: Result<TokenStream> = Err(e);
844+
return handle_reg_error(
845+
"Error generating register definition for a register cluster",
846+
*reg,
847+
res,
848+
);
849+
}
850+
};
826851
}
827852

828853
// Generate the sub-cluster blocks.
@@ -848,7 +873,7 @@ fn expand_svd_register(
848873
register: &Register,
849874
name: Option<&str>,
850875
ignore_group: bool,
851-
) -> Result<Vec<syn::Field>, syn::Error> {
876+
) -> Result<Vec<syn::Field>> {
852877
let mut out = vec![];
853878

854879
match register {
@@ -885,13 +910,14 @@ fn convert_svd_register(
885910
register: &Register,
886911
name: Option<&str>,
887912
ignore_group: bool,
888-
) -> Result<syn::Field, syn::Error> {
913+
) -> Result<syn::Field> {
889914
Ok(match register {
890915
Register::Single(info) => {
891916
let info_name = info.fullname(ignore_group);
892917
new_syn_field(
893918
&info_name.to_sanitized_snake_case(),
894-
name_to_wrapped_ty(&info_name, name)?,
919+
name_to_wrapped_ty(&info_name, name)
920+
.with_context(|| format!("Error converting info name {}", info_name))?,
895921
)
896922
}
897923
Register::Array(info, array_info) => {
@@ -1038,7 +1064,16 @@ fn name_to_wrapped_ty_str(name: &str, ns: Option<&str>) -> String {
10381064
}
10391065
}
10401066

1041-
fn name_to_wrapped_ty(name: &str, ns: Option<&str>) -> Result<syn::Type, syn::Error> {
1067+
fn name_to_wrapped_ty(name: &str, ns: Option<&str>) -> Result<syn::Type> {
10421068
let ident = name_to_wrapped_ty_str(name, ns);
1043-
Ok(syn::Type::Path(parse_str::<syn::TypePath>(&ident)?))
1069+
match parse_str::<syn::TypePath>(&ident) {
1070+
Ok(path) => Ok(syn::Type::Path(path)),
1071+
Err(e) => {
1072+
let mut res = Err(e.into());
1073+
res = res.with_context(|| {
1074+
format!("Determining syn::TypePath from ident \"{}\" failed", ident)
1075+
});
1076+
res
1077+
}
1078+
}
10441079
}

src/main.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![recursion_limit = "128"]
22

3-
use log::error;
3+
use log::{error, info};
44
use std::path::PathBuf;
55
use svd_parser::svd;
66

@@ -164,10 +164,15 @@ fn run() -> Result<()> {
164164
svd::ValidateLevel::Weak
165165
};
166166

167-
let device = svd_parser::parse_with_config(xml, &parser_config)?;
167+
info!("Parsing device from SVD file");
168+
let device = svd_parser::parse_with_config(xml, &parser_config)
169+
.with_context(|| "Error parsing SVD file".to_string())?;
168170

169171
let mut device_x = String::new();
170-
let items = generate::device::render(&device, &config, &mut device_x)?;
172+
info!("Rendering device");
173+
let items = generate::device::render(&device, &config, &mut device_x)
174+
.with_context(|| "Error rendering device")?;
175+
171176
let filename = if make_mod { "mod.rs" } else { "lib.rs" };
172177
let mut file = File::create(path.join(filename)).expect("Couldn't create output file");
173178

src/util.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use proc_macro2::{Ident, Literal, Span, TokenStream};
66
use quote::{quote, ToTokens};
77
use std::path::PathBuf;
88

9-
use anyhow::{anyhow, bail, Result};
9+
use anyhow::{anyhow, bail, Context, Result};
1010

1111
pub const BITS_PER_BYTE: u32 = 8;
1212

@@ -370,6 +370,22 @@ pub fn build_rs() -> TokenStream {
370370
}
371371
}
372372

373+
pub fn handle_reg_error<T>(msg: &str, reg: &Register, res: Result<T>) -> Result<T> {
374+
let reg_name = &reg.name;
375+
let descrip = reg.description.as_deref().unwrap_or("No description");
376+
handle_erc_error(msg, reg_name, descrip, res)
377+
}
378+
379+
pub fn handle_cluster_error<T>(msg: &str, cluster: &Cluster, res: Result<T>) -> Result<T> {
380+
let cluster_name = &cluster.name;
381+
let descrip = cluster.description.as_deref().unwrap_or("No description");
382+
handle_erc_error(msg, cluster_name, descrip, res)
383+
}
384+
385+
fn handle_erc_error<T>(msg: &str, name: &str, descrip: &str, res: Result<T>) -> Result<T> {
386+
res.with_context(|| format!("{}\nName: {}\nDescription: {}", msg, name, descrip))
387+
}
388+
373389
pub trait FullName {
374390
fn fullname(&self, ignore_group: bool) -> Cow<str>;
375391
}

0 commit comments

Comments
 (0)