1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
// Copyright 2021 Ian Jackson and contributors
// SPDX-License-Identifier: GPL-3.0-or-later
// There is NO WARRANTY.
#![feature(exit_status_error)]
//! 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 borred 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,
}
}
}