Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- [TokenStream](./procedural_macros/token_stream.md)
- [proc-macro2, syn, quote](./procedural_macros/proc_macro2_syn_quote.md)
- [Syntax Tree](./procedural_macros/syntax_tree.md)
- [Rust Syntax Tree](./procedural_macros/syntax_tree/rust_syntax_tree.md)
- [Span](./procedural_macros/span.md)
- [quote](./procedural_macros/quote.md)
- [Procedural Macro Crates](./procedural_macros/proc_macro_crates.md)
48 changes: 31 additions & 17 deletions src/procedural_macros/syntax_tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,22 @@ use syn::*;
fn main() {
// Parse the `pub` keyword
let input = quote::quote! {pub};
let _token: Token![pub] = parse2(input).unwrap();
let t: Token![pub] = parse2(input).unwrap();
println!("{t:?}");
// Or use parse_quote!
let _token: Token![pub] = parse_quote! {pub};

let t: Token![pub] = parse_quote! {pub};
println!("{t:?}");
// Parse the `struct` keyword
let _token: Token![struct] = parse_quote! {struct};

let t: Token![struct] = parse_quote! {struct};
println!("{t:?}");
// Parse `+=`
let _token: Token![+=] = parse_quote! {+=};

let t: Token![+=] = parse_quote! {+=};
println!("{t:?}");
// Parse `::`
let _token: Token![::] = parse_quote! {::};

let t: Token![::] = parse_quote! {::};
println!("{t:?}");
// Error: `pub fn main() {}` is not a single token
// let _token: Token![pub] = parse_quote! {pub fn main() {}};
// let t: Token![pub] = parse_quote! {pub fn main() {}};
}
```

Expand All @@ -72,7 +73,8 @@ mod kw{
}

fn main() {
let _token: kw::div = parse_quote! {div};
let t: kw::div = parse_quote! {div};
println!("{t:?}");
}
```

Expand All @@ -82,11 +84,15 @@ fn main() {
use syn::*;

fn main() {
let _node: ItemFn = parse_quote! {fn main() {println!("Hello, world!")}};
let _node: ItemStruct = parse_quote! {struct MyStruct {field: i32}};
let node: ItemFn = parse_quote! {fn main() {println!("Hello, world!")}};
println!("{node:#?}");
let node: ItemStruct = parse_quote! {struct MyStruct {field: i32}};
println!("{node:#?}");
// `syn::DeriveInput` is a syntax tree node that represents any valid input to a derive macro.
let _node: DeriveInput = parse_quote! {#[derive(Debug)] struct MyStruct {field: i32}};
let node: DeriveInput = parse_quote! {#[derive(Debug)] struct MyStruct {field: i32}};
println!("{node:#?}");
}

```

## Parsing a Custom Syntax Tree Node
Expand All @@ -104,9 +110,14 @@ use syn::{
parse::{ParseStream, Parser},
*,
};
// We define custom keywords in a `kw` or `keywords` module by convention.
mod kw {
syn::custom_keyword!(div);
}

fn main() {
let input = quote! {
// Tip: try modifying it to an invalid div element and see the result.
<div>"Hello World"</div>
};
// parse::Parser::parse2(|input: ParseStream| -> Result<()> { todo!() }, input).unwrap();
Expand All @@ -118,23 +129,26 @@ fn main() {
// `<`
input.parse::<Token![<]>()?;
// `div`
input.parse::<Ident>()?;
input.parse::<kw::div>()?;
// `>`
input.parse::<Token![>]>()?;
// `"Hello World"`
input.parse::<LitStr>()?;
let str = input.parse::<LitStr>()?;
// `<`
input.parse::<Token![<]>()?;
// `/`
input.parse::<Token![/]>()?;
// `div`
input.parse::<Ident>()?;
input.parse::<kw::div>()?;
// `>`
input.parse::<Token![>]>()?;
println!("{str:?}");
println!("Done!");
Ok(())
};
parser.parse2(input).unwrap();
}

```

### Defining a custom syntax tree node type by implementing the `syn::parse::Parse` trait
Expand Down
66 changes: 66 additions & 0 deletions src/procedural_macros/syntax_tree/rust_syntax_tree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Rust Syntax Tree

We've learned how to implement our own syntax tree nodes for virtually any DSL. But if we only want to work with valid Rust code using `syn`, we don't need to implement any custom nodes. The good news is that you don't even need to learn anything beyond what this tutorial has already covered about `syn` to handle concrete Rust-code-related tasks.

Here is a simple example to get you started.

# Ok Type Extraction

Let's implement a function that parses a Rust function and extracts the inner success type (Ok type) from its Result return type and prints it to the console.

> [!TIP]
>
> You don't need to read the following implementation. Just run it and observe the output.

```rust,editable,compile_fail
use proc_macro2::TokenStream;
use syn::ItemFn;

fn main() {
ok_type(syn::parse_quote! {fn foo() -> i32 {}});
ok_type(syn::parse_quote! {fn foo() -> Result<i32> {}});
ok_type(syn::parse_quote! {fn add(a:u32,b:u32) -> Result<u32, Error> {}});
}

fn ok_type(item_fn: TokenStream) {
println!(r#"fn: "{}""#, item_fn.to_string());
let item_fn: ItemFn = syn::parse_quote! {#item_fn};
let syn::ReturnType::Type(_, return_type) = item_fn.sig.output else {
println!("Ok Type Unknown.");
return;
};
let syn::Type::Path(return_type) = return_type.as_ref() else {
println!("Ok Type Unknown.");
return;
};
let syn::PathArguments::AngleBracketed(generic_args) =
&return_type.path.segments.first().unwrap().arguments
else {
println!("Ok Type Unknown.");
return;
};
let syn::GenericArgument::Type(syn::Type::Path(ok_type)) = generic_args.args.first().unwrap()
else {
println!("Ok Type Unknown.");
return;
};
println!("Ok type is: {}", ok_type.path.get_ident().unwrap());
}
```

The above code uses a few `syn` nodes. How do you know in advance which nodes you need to implement your own `ok_type` function? The answer is: no, you don't need to know anything.

You just need to start at the very top node — `ItemFn` for this example. Print its debug output, then figure out the "happy path" that leads to your task target.

For example:

```rust,editable,compile_fail
use syn::ItemFn;

fn main() {
let item_fn: ItemFn = syn::parse_quote! {fn foo() -> Result<i32> {}};
println!("{item_fn:#?}");
}
```

Then follow the "happy path" all the way to your target — `i32` in this example. The golden rule is: focus only on the "happy-path".