0

For the sake of this example: I want to write a proc macro attribute which modifies all number literals in a function body.

I have a syn::Block from a syn::FnItem. I want to map all over the tokens in the block and change literals. I currently have:

let token_stream = func
    .block
    .to_token_stream()
    .into_iter()
    .map(|token| ...)

func.block = Box::new(syn::parse2::<Block>(token_stream.collect()).unwrap());

The problem is to_token_stream from Quote::ToTokens returns a proc_macro2::TokenStream and its into_iter item is proc_macro2::TokenTree which is a nested representation of the tokens.

So for the block representing 2 + (3 + 4)

The iterator is over the following single item:

Group {
    delimiter: Brace,
    stream: TokenStream [
        Literal {
            kind: Integer,
            symbol: "2",
            suffix: None,
            span: #0 bytes(209..210),
        },
        Punct {
            ch: '+',
            spacing: Alone,
            span: #0 bytes(211..212),
        },
        Group {
            delimiter: Parenthesis,
            stream: TokenStream [
                Literal {
                    kind: Integer,
                    symbol: "3",
                    suffix: None,
                    span: #0 bytes(214..215),
                },
                Punct {
                    ch: '+',
                    spacing: Alone,
                    span: #0 bytes(216..217),
                },
                Literal {
                    kind: Integer,
                    symbol: "4",
                    suffix: None,
                    span: #0 bytes(218..219),
                },
            ],
            span: #0 bytes(213..220),
        },
    ],
    span: #0 bytes(203..222),
}

Where the tokens are deeply nested in the structure.

I need a flat representation that would look like:

Literal("2")
Plus
ParenthesisOpen
Literal("3")
Plus
Literal("4")
ParenthesisClose

Is this possible? I could write my own flat_map thinh to go before the map to make it work but that is a lot of work. Does syn expose anything to do this? Maybe I should not parse it into a syn::Block before I do the transform...?

Ben
  • 3,160
  • 3
  • 17
  • 34

1 Answers1

0

Turns out that syn has a mutable visiting module. Rather than touching the token stream you can use a recursive visitor like so:

use syn::{visit_mut::{self, VisitMut}, LitInt};

struct MyNumberLiteralModifier;

impl VisitMut for MyNumberLiteralModifier {
    fn visit_lit_int_mut(&mut self, node: &mut LitInt) {
        *node = LitInt::new("10", node.span());   
    }
}

MyNumberLiteralModifier.walk_block(&mut my_block);

To do so "visit-mut" feature must be enabled for syn

Ben
  • 3,160
  • 3
  • 17
  • 34