partial-borrow-macros 1.0.1

proc-macros for partial-borrow
Documentation
// Copyright 2021 Ian Jackson and contributors
// SPDX-License-Identifier: GPL-3.0-or-later
// There is NO WARRANTY.

//! Macros for `partial_borrow`.
//!
//! See that crate for documentation.
//! NB that the **links in this view may be broken!**

use std::convert::TryInto;
use std::env;
use std::ffi::OsString;
use std::fmt;
use std::fs::File;
use std::io::BufRead as _;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Seek as _;
use std::io::Write as _;
use std::iter;
use std::num::NonZeroU8;
use std::process::Command;
use std::str;

use darling::{FromDeriveInput};
use fehler::{throw, throws};
use indexmap::IndexMap;
use itertools::Itertools;
use proc_macro2::Span as Span2;
use proc_macro2::TokenStream as TokenStream2;
use proc_macro_error::{abort_call_site, emit_error, proc_macro_error};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use syn::DeriveInput;
use syn::Token;
use syn::parse::{Parse, ParseStream};
use syn::parse_macro_input;

static DEBUG_ENVVAR: &str = "RUST_PARTIAL_BORROW_EXPAND_DEBUG";
static STDERR: &str = "/dev/stderr";

fn ourselves_ident() -> syn::Ident { format_ident!("partial_borrow")}

fn default<T:Default>() -> T { Default::default() }

static PERMITS: [&str;3] = ["No","Const","Mut"];
#[derive(Debug,Copy,Clone,)]
enum Permission { No, Const, Mut }
impl quote::IdentFragment for Permission {
  #[throws(fmt::Error)]
  fn fmt(&self, f: &mut fmt::Formatter) { write!(f, "{:?}", self)? }
}


mod derive;
mod partial;

/// **Derive partial borrowing for a struct.**
///
/// # Invocation details
///
/// Must be applied to a sized braced struct ("`Struct`", below)
/// (By "braced struct" we mean what the Reference calls a
/// `StructStruct`.)
///
/// The invocation scope **must** have imported the `partial_borrow`
/// crate, under that name.
///
/// Takes the additional attribute `#[partial_borrow(...)]`
/// where `...` may be some of the following parameters:
///
///  * `Debug`: Generates a `Debug` impl for the partial struct
///    (and impls for its field accessor proxies).  Inaccessible fields
///    are debug printed as `_`.
///
///  * `partial="Partial"`: The permission-parameterised partially
///    borrowed struct will be called `Partial` instead of
///    `Struct__Partial`.
///
///  * `module="Module"`: The module containing field definitions
///    will be called `Module` instead of `Struct__`.
///
///  * `suffix="identchars"`: `identchars` will be used instead of
///    `__` in generated identifiers, including local variables in
///    generated functions, added type and lifetime parameters, and
///    the default values of `partial` and `module`.
///
///    See Injected names, below.
///
///  * `imported_for_test_unsafe`: Indicates that the invocation scope has
///    `use partial_borrow::imports::*`.  The generated code will just
///    say `foo` rather than `partial_borrow::imports:foo`.  This is
///    useful for testing and for review by humans.
///
///    **Safety!** For soundness, the derived code's uses of
///    now-unqualified names must always refer to the traits and types in
///    `partial_borrow::imports`.  This is difficult to ensure in
///    nontrivial programs.  This feature is provided for maintenance,
///    testing, diagnostics, etc.
///
/// # Generated items
///
/// This derive macro introduces two names and many trait
/// implementations into the invoking scope.  Both the named items
/// have the same visibility as `Struct`.
///
/// ## Partially borrowed struct view
///
/// ```ignore
/// struct Struct__Partial<...> { ... }
/// ```
///
/// `Struct__Partial` is generic over all the same things as `Struct`,
/// but additionally, just after the lifetimes, it has additional
/// type parameters: one `P__field` for each field named `field`.
///
/// You can never have an owned `Struct__Partial`, just borrows of
/// it.  Such a borrow represents a borrow of each individual field,
/// according to the permission type parameter `P__field` (which will
/// be one of
/// [`Mut`](perms/struct.Mut.html)
/// [`Const`](perms/struct.Const.html)
/// [`No`](perms/struct.No.html)).
///
/// Fields with permission `Const` are only immutably accessible, even
/// via an `&mut Struct_Partial`.  Fields with permission `Mut` are
/// mutably accessible, but of course only if you have an `&mut
/// Struct_Partial`.  Fields with permission `No` are inaccessible.
///
/// Each field of the borrow is a smart pointer `Struct__::F_field`
/// which (when allowed) dereferences to the actual field.  So
/// accessing fields other than by method call typically involves an
/// additional `*`.
///
/// ## impls
///
///  * [`Downgrade`](trait.Downgrade.html) and
///    [`AsRef`](core::convert::AsRef) and
///    [`AsMut`](core::convert::AsMut), to
///    make references to partial structs from more powerful
///    references.
///
///  * [`SplitOff`](trait.SplitOff.html),
///    [`SplitInto`](trait.SplitInto.html)
///    and [`From`]
///    to divide references into
///    mutually-compatible partial struct views.
///
///  * If the `Debug` parameter was specified,
///    [`Debug`](std::fmt::Debug) for
///    `Struct_Partial` and for field accessors `Struct__::F__field`.
///
///  * [`Deref`](std::ops::Deref) impl which allows any complete, but perhaps
///    partially-immutable, struct view, to deref back to an
///    `&Struct`.  (I.e., this works for any partial view without any
///    `No` permissions, only `Const` and `Mut` ones.)
///
///  * [`Deref`](std::ops::Deref) and [`DerefMut`](std::ops::DerefMut)
///    impls for the field accessors.
///
/// Note that the downgrade and splitting traits are implemented for
/// `&Struct` and `&mut Struct` as well: you make partial struct view
/// reference(s) from a complete struct reference the same way you
/// do from a partial struct reference.
///
/// ### Implementation of other traits for partial structs
///
/// Traits which do not understand partial structs are generally not
/// implemented (or implementable) for them.  Trait methods that only
/// take `&self` can be called via the `Deref` if all of the fields
/// are at least `Const`-accessible.
///
/// In principle it would be possible to provide a facility to apply
/// `#[derive]` attributes to the generated struct.  But it would not
/// be possible to pass any attributes (on the struct or any fields),
/// because the attributes might be macros which would alter the
/// generated `Struct__Partial`, whose shape is important for the
/// soundness.
///
/// Trait and inherent impls on partial structs are fine and can be
/// useful.
///
/// ## Module
///
/// ```ignore
/// mod Struct__ {
///     struct F_field<Permission, FieldType, Struct> { ... }
/// }
/// ```
///
/// This contains helper items.  Notably, the `F_field` type for each
/// field `field`, which dereferences to the value.
/// Like `Struct__Partial`, it is not possible to obtain an owned
/// field accessor struct, just references.
///
/// # Injected names
///
/// Many of the impls require additional generic parameters,
/// all of which are generated as `X__` or `X__field` or `'x__`
/// for some `X` or `x`.
///
/// Here is a full list of the generic parameters injected anywhere
/// where they might clash with your names:
/// `'r__ P__ S__ T__ N__ R__ P__field R__field S__field`.
/// But: note that additions to this list will *not* be considered a semver
/// break.
///
/// All this will only trouble you if you derive `PartialBrorrow` on a
/// struct which has other generic parameters containing `__`.
/// If any injected names might clash with other generics for your struct,
/// use the `suffix` paramrter to change `__` to something else.
///
/// (All this is in addition to the generated items `Struct__Partial`
/// `Struct__`, documented above.)

#[proc_macro_derive(PartialBorrow, attributes(partial_borrow))]
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
  derive::derive(input)
}

/// **Conveniently specify a partially borrowed struct type**,
/// by field(s).
///
/// # Syntax overview
///
/// Roughly
/// ```ignore
/// type X = partial!( Struct mut/const/! field field... , 
///                           mut/const/! field field... * ,
///                           ... );
/// ```
/// More formally
/// ```text
/// Input      := BaseType FieldGroup,*
/// BaseType   := RustReference::TypePath
/// FieldGroup := Permission FieldSpec*
/// Permission := 'mut' | 'const' | '!'
/// FieldSpec  := '*' | RustReference::IDENTIFIER
/// ```
/// # Examples
/// ```ignore
/// struct Struct { a:char, b:char, c:char, d:char, e:char };
/// type X = partial!( Struct mut a b c, const d, ! *);
/// type Y = partial!( Struct mut a ); // a is Mut, others are Const
/// ```
///
/// # Interpretation
///
/// `partial!()` expands to the type of a partial view of `Struct`,
/// according to the specified permissions.
///
/// Each list of fields starts with a a permission:
/// `mut` ([`Mut`](perms/struct.Mut.html)),
/// `const` ([`Const`](perms/struct.Const.html)) or 
/// `!` ([`No`](perms/struct.No.html)).
/// The field *lists* (not the
/// individual fields) are separated by commas.
///
/// `*` may appear once, in any group, and specifies the permission
/// for fields not explicitly mentioned.  If there is no `*`, `Const`
/// is used.
///
/// Each field may appear only once.  Ordering is not relevant.
///
/// # Expansion
///
/// Use of `partial!()` can be replaced by a plain concrete type
/// `Struct__Partial<P_field0, P_field1, P_field2, ...>`.
/// where `P__...` are `Mut`, `Const` or `No`, one per field.
///
/// For Reasons, the actual expansion of `partial!()` is something
/// much more complicated and strange involving the `Adjust` trait
/// impl and `Struct__::FIELDS` constant, both generated by the derive.
///
/// # `impl partial!(...) { ... }`
///
/// Rust does not support inherent impls on a type derived from a
/// complicated chain of trait lookups, which is what `partial!()`
/// produces.
///
/// If you want to make a method that takes a partial struct, you can:
///
///  * Use an extension trait. e.g. via `easy_ext`
///    (this is quite convenient).
///  * Write out the `Struct__Partial` type directly, listing one
///  `Mut` `Const` or `No` type paramter for each field in order;
///  * Use a newtype and impl on that;
///  * Make it a plain function rather than a method.
///
/// # Safety, and the field permission and borrowck systems
///
/// `partial()` allows you to name a type, for example for a variable
/// or function parameter.  It does not do any access control
/// (borrowck consistency) checks.  Those are done later, after the
/// meaning of `partial!()` has been determined.
///
/// If what you ask for with your uses of `partial()`, reference
/// reborrowing, etc., does not satisfy the rules, you will get a
/// compile error.
#[proc_macro]
pub fn partial(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
  partial::partial(input)
}

enum DebugMode<'s> {
  Stderr(NonZeroU8),
  Write(&'s str),
  Compare(&'s str),
}

struct DebugVar(Option<OsString>);
impl DebugVar {
  pub fn new() -> Self { Self(env::var_os(DEBUG_ENVVAR)) }

  pub fn mode(&self) -> Option<DebugMode> {
    use DebugMode::*;
    let ev = self.0.as_ref()?;
    let ev = ev.to_str().expect(DEBUG_ENVVAR);
    if let Ok::<u8,_>(level) = ev.parse() {
      level.try_into().map(Stderr).ok()
    }
    else if let Some(rest) = ev.strip_prefix("<") { Some(Compare(rest)) }
    else if let Some(rest) = ev.strip_prefix(">") { Some(Write(rest)) }
    else { panic!("did not understand {}", DEBUG_ENVVAR) }
  }    
}

impl DebugMode<'_> {
  pub fn wants(dhow: &Option<Self>, mverbosity: u8) -> bool {
    use DebugMode::*;
    match dhow {
      Some(Stderr(level)) => u8::from(*level) >= mverbosity,
      None | Some(Write(_)) | Some(Compare(_)) => false,
    }
  }
}