Skip to main content

salsa_macros/
lib.rs

1//! This crate provides salsa's macros and attributes.
2
3#![recursion_limit = "256"]
4
5#[macro_use]
6extern crate quote;
7
8use proc_macro::TokenStream;
9
10macro_rules! parse_quote {
11    ($($inp:tt)*) => {
12        {
13            let tt = quote!{$($inp)*};
14            syn::parse2(tt.clone()).unwrap_or_else(|err| {
15                panic!("failed to parse `{}` at {}:{}:{}: {}", tt, file!(), line!(), column!(), err)
16            })
17        }
18    }
19}
20
21/// Similar to `syn::parse_macro_input`, however, when a parse error is encountered, it will return
22/// the input token stream in addition to the error. This will make it so that rust-analyzer can work
23/// with incomplete code.
24macro_rules! parse_macro_input {
25    ($tokenstream:ident as $ty:ty) => {
26        match syn::parse::<$ty>($tokenstream.clone()) {
27            Ok(data) => data,
28            Err(err) => {
29                return $crate::token_stream_with_error($tokenstream, err);
30            }
31        }
32    };
33}
34
35mod accumulator;
36mod db;
37mod db_lifetime;
38mod debug;
39mod fn_util;
40mod hygiene;
41mod input;
42mod interned;
43mod options;
44mod salsa_struct;
45mod supertype;
46mod tracked;
47mod tracked_fn;
48mod tracked_impl;
49mod tracked_struct;
50mod update;
51mod xform;
52
53#[proc_macro_attribute]
54pub fn accumulator(args: TokenStream, input: TokenStream) -> TokenStream {
55    accumulator::accumulator(args, input)
56}
57
58#[proc_macro_attribute]
59pub fn db(args: TokenStream, input: TokenStream) -> TokenStream {
60    db::db(args, input)
61}
62
63#[proc_macro_attribute]
64pub fn interned(args: TokenStream, input: TokenStream) -> TokenStream {
65    interned::interned(args, input)
66}
67
68#[proc_macro_derive(Supertype)]
69pub fn supertype(input: TokenStream) -> TokenStream {
70    supertype::supertype(input)
71}
72
73#[proc_macro_attribute]
74pub fn input(args: TokenStream, input: TokenStream) -> TokenStream {
75    input::input(args, input)
76}
77
78#[proc_macro_attribute]
79pub fn tracked(args: TokenStream, input: TokenStream) -> TokenStream {
80    tracked::tracked(args, input)
81}
82
83/// Derives `salsa::Update` for a struct or enum.
84///
85/// Generic type parameters receive an implicit `salsa::Update` bound unless all their uses appear
86/// in fields that use the `fallback` or `unsafe(with(...))` update strategies.
87///
88/// The `#[update(...)]` field helper supports these forms:
89///
90/// - `#[update(fallback)]` updates the field with `salsa::update_fallback`.
91///   This form adds a `FieldTy: 'static + PartialEq` bound to the generated impl.
92/// - `#[update(unsafe(with(expr)))]` updates the field with `expr`, which must have type
93///   `unsafe fn(*mut FieldTy, FieldTy) -> bool`. The caller is responsible for
94///   ensuring the custom function upholds the `salsa::Update` safety contract.
95/// - `#[update(bounds(Predicate, ...))]` adds one or more where-predicates to the generated impl.
96///   This form can be combined with `fallback` or `unsafe(with(...))`; by itself it does not change
97///   how the field is updated.
98///
99/// # Examples
100///
101/// ```ignore
102/// #[derive(Clone, PartialEq, Eq, salsa::Update)]
103/// struct Foo<T> {
104///     value: T,
105/// }
106/// ```
107///
108/// Since `value` uses the normal update path, the generated impl requires `T: salsa::Update`.
109///
110/// ```ignore
111/// #[derive(Clone, PartialEq, Eq, salsa::Update)]
112/// struct Foo<T> {
113///     #[update(fallback)]
114///     value: T,
115/// }
116/// ```
117///
118/// The `fallback` helper uses `salsa::update_fallback` and requires `T: 'static + PartialEq`
119/// instead of `T: salsa::Update`.
120///
121/// ```ignore
122/// #[derive(Clone, PartialEq, Eq, salsa::Update)]
123/// struct Foo<T, U> {
124///     #[update(bounds(T: 'static + PartialEq, Vec<U>: Clone), unsafe(with(custom_update::<T>)))]
125///     value: T,
126///     marker: std::marker::PhantomData<U>,
127/// }
128///
129/// unsafe fn custom_update<T>(old: *mut T, new: T) -> bool
130/// where
131///     T: 'static + PartialEq,
132/// {
133///     salsa::update_fallback(old, new)
134/// }
135/// ```
136#[proc_macro_derive(Update, attributes(update))]
137pub fn update(input: TokenStream) -> TokenStream {
138    let item = parse_macro_input!(input as syn::DeriveInput);
139    match update::update_derive(item) {
140        Ok(tokens) => tokens.into(),
141        Err(error) => error.into_compile_error().into(),
142    }
143}
144
145pub(crate) fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
146    tokens.extend(TokenStream::from(error.into_compile_error()));
147    tokens
148}
149
150mod kw {
151    syn::custom_keyword!(bounds);
152    syn::custom_keyword!(fallback);
153    syn::custom_keyword!(with);
154    syn::custom_keyword!(maybe_update);
155}