machine_check_bitmask_switch/
lib.rs

1#![doc = include_str!("../README.md")]
2
3extern crate proc_macro;
4
5mod arm;
6mod types;
7mod util;
8
9use arm::process_arms;
10pub use types::{BitmaskArm, BitmaskArmChoice, BitmaskSwitch};
11
12use num::BigUint;
13use proc_macro2::{Span, TokenStream};
14use quote::quote;
15use syn::spanned::Spanned;
16use syn::token::Brace;
17use syn::{parse2, Block, Ident, Local, LocalInit, Pat, PatIdent, Stmt, Token};
18use util::{convert_type, create_number_expr};
19
20pub fn process(stream: TokenStream) -> Result<TokenStream, Error> {
21    let switch: BitmaskSwitch = parse2(stream).map_err(Error::Parse)?;
22    generate(switch)
23}
24
25#[derive(Debug)]
26pub enum Error {
27    Parse(syn::parse::Error),
28    Process(String, Span),
29}
30
31impl Error {
32    pub fn msg(&self) -> String {
33        match self {
34            Error::Parse(error) => error.to_string(),
35            Error::Process(msg, _span) => msg.clone(),
36        }
37    }
38}
39
40pub fn generate(switch: BitmaskSwitch) -> Result<TokenStream, Error> {
41    let scrutinee_span = switch.expr.span();
42    // mixed site ident as we do not want the caller to know about it
43    let scrutinee_ident = Ident::new("__scrutinee", Span::mixed_site());
44    let something_taken_ident = Ident::new("__something_taken", Span::mixed_site());
45
46    // process arms
47    // we need to do this before creating the scrutinee statement
48    // as the arms determine the number of bits
49    let (arm_stmts, num_bits) = process_arms(
50        scrutinee_ident.clone(),
51        something_taken_ident.clone(),
52        switch.arms,
53        switch.brace_token.span.span(),
54    )?;
55
56    // add local statements to outer block
57    let scrutinee_local = Stmt::Local(Local {
58        attrs: vec![],
59        let_token: Token![let](scrutinee_span),
60        pat: Pat::Ident(PatIdent {
61            attrs: vec![],
62            by_ref: None,
63            mutability: None,
64            ident: scrutinee_ident,
65            subpat: None,
66        }),
67        init: Some(LocalInit {
68            eq_token: Token![=](scrutinee_span),
69            expr: Box::new(convert_type(*switch.expr, num_bits, scrutinee_span, true)),
70            diverge: None,
71        }),
72        semi_token: Token![;](scrutinee_span),
73    });
74    let something_taken_local = Stmt::Local(Local {
75        attrs: vec![],
76        let_token: Token![let](scrutinee_span),
77        pat: Pat::Ident(PatIdent {
78            attrs: vec![],
79            by_ref: None,
80            mutability: Some(Token![mut](scrutinee_span)),
81            ident: something_taken_ident,
82            subpat: None,
83        }),
84        init: Some(LocalInit {
85            eq_token: Token![=](scrutinee_span),
86            expr: Box::new(create_number_expr(&BigUint::from(0u8), 1, scrutinee_span)),
87            diverge: None,
88        }),
89        semi_token: Token![;](scrutinee_span),
90    });
91
92    // add scrutinee, something-taken, and arms to outer block
93    let mut outer_block = Block {
94        brace_token: Brace {
95            span: switch.brace_token.span,
96        },
97        stmts: vec![scrutinee_local, something_taken_local],
98    };
99
100    outer_block.stmts.extend(arm_stmts);
101
102    let expanded = quote! {
103        #outer_block
104    };
105
106    Ok(expanded)
107}