salsa-macros 0.27.1

Procedural macros for the salsa crate
Documentation
//! This crate provides salsa's macros and attributes.

#![recursion_limit = "256"]

#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

macro_rules! parse_quote {
    ($($inp:tt)*) => {
        {
            let tt = quote!{$($inp)*};
            syn::parse2(tt.clone()).unwrap_or_else(|err| {
                panic!("failed to parse `{}` at {}:{}:{}: {}", tt, file!(), line!(), column!(), err)
            })
        }
    }
}

/// Similar to `syn::parse_macro_input`, however, when a parse error is encountered, it will return
/// the input token stream in addition to the error. This will make it so that rust-analyzer can work
/// with incomplete code.
macro_rules! parse_macro_input {
    ($tokenstream:ident as $ty:ty) => {
        match syn::parse::<$ty>($tokenstream.clone()) {
            Ok(data) => data,
            Err(err) => {
                return $crate::token_stream_with_error($tokenstream, err);
            }
        }
    };
}

mod accumulator;
mod db;
mod db_lifetime;
mod debug;
mod fn_util;
mod hygiene;
mod input;
mod interned;
mod options;
mod salsa_struct;
mod supertype;
mod tracked;
mod tracked_fn;
mod tracked_impl;
mod tracked_struct;
mod update;
mod xform;

#[proc_macro_attribute]
pub fn accumulator(args: TokenStream, input: TokenStream) -> TokenStream {
    accumulator::accumulator(args, input)
}

#[proc_macro_attribute]
pub fn db(args: TokenStream, input: TokenStream) -> TokenStream {
    db::db(args, input)
}

#[proc_macro_attribute]
pub fn interned(args: TokenStream, input: TokenStream) -> TokenStream {
    interned::interned(args, input)
}

#[proc_macro_derive(Supertype)]
pub fn supertype(input: TokenStream) -> TokenStream {
    supertype::supertype(input)
}

#[proc_macro_attribute]
pub fn input(args: TokenStream, input: TokenStream) -> TokenStream {
    input::input(args, input)
}

#[proc_macro_attribute]
pub fn tracked(args: TokenStream, input: TokenStream) -> TokenStream {
    tracked::tracked(args, input)
}

/// Derives `salsa::Update` for a struct or enum.
///
/// Generic type parameters receive an implicit `salsa::Update` bound unless all their uses appear
/// in fields that use the `fallback` or `unsafe(with(...))` update strategies.
///
/// The `#[update(...)]` field helper supports these forms:
///
/// - `#[update(fallback)]` updates the field with `salsa::update_fallback`.
///   This form adds a `FieldTy: 'static + PartialEq` bound to the generated impl.
/// - `#[update(unsafe(with(expr)))]` updates the field with `expr`, which must have type
///   `unsafe fn(*mut FieldTy, FieldTy) -> bool`. The caller is responsible for
///   ensuring the custom function upholds the `salsa::Update` safety contract.
/// - `#[update(bounds(Predicate, ...))]` adds one or more where-predicates to the generated impl.
///   This form can be combined with `fallback` or `unsafe(with(...))`; by itself it does not change
///   how the field is updated.
///
/// # Examples
///
/// ```ignore
/// #[derive(Clone, PartialEq, Eq, salsa::Update)]
/// struct Foo<T> {
///     value: T,
/// }
/// ```
///
/// Since `value` uses the normal update path, the generated impl requires `T: salsa::Update`.
///
/// ```ignore
/// #[derive(Clone, PartialEq, Eq, salsa::Update)]
/// struct Foo<T> {
///     #[update(fallback)]
///     value: T,
/// }
/// ```
///
/// The `fallback` helper uses `salsa::update_fallback` and requires `T: 'static + PartialEq`
/// instead of `T: salsa::Update`.
///
/// ```ignore
/// #[derive(Clone, PartialEq, Eq, salsa::Update)]
/// struct Foo<T, U> {
///     #[update(bounds(T: 'static + PartialEq, Vec<U>: Clone), unsafe(with(custom_update::<T>)))]
///     value: T,
///     marker: std::marker::PhantomData<U>,
/// }
///
/// unsafe fn custom_update<T>(old: *mut T, new: T) -> bool
/// where
///     T: 'static + PartialEq,
/// {
///     salsa::update_fallback(old, new)
/// }
/// ```
#[proc_macro_derive(Update, attributes(update))]
pub fn update(input: TokenStream) -> TokenStream {
    let item = parse_macro_input!(input as syn::DeriveInput);
    match update::update_derive(item) {
        Ok(tokens) => tokens.into(),
        Err(error) => error.into_compile_error().into(),
    }
}

pub(crate) fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
    tokens.extend(TokenStream::from(error.into_compile_error()));
    tokens
}

mod kw {
    syn::custom_keyword!(bounds);
    syn::custom_keyword!(fallback);
    syn::custom_keyword!(with);
    syn::custom_keyword!(maybe_update);
}