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
/*
* Copyright 2019 The Starlark in Rust Authors.
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! A proc-macro for writing functions in Rust that can be called from Starlark.
#![cfg_attr(feature = "gazebo_lint", feature(plugin))]
#![cfg_attr(feature = "gazebo_lint", allow(deprecated))] // :(
#![cfg_attr(feature = "gazebo_lint", plugin(gazebo_lint))]
#![feature(box_patterns)]
#[allow(unused_extern_crates)] // proc_macro is very special
extern crate proc_macro;
use proc_macro::TokenStream;
use syn::*;
mod attrs;
mod bc;
mod freeze;
mod parse;
mod render;
mod serde;
mod trace;
mod typ;
mod util;
/// Write Starlark modules concisely in Rust syntax.
///
/// For example:
///
/// ```ignore
/// #[starlark_module]
/// fn global(registry: &mut GlobalsBuilder) {
/// fn cc_binary(name: &str, srcs: Vec<&str>) -> String {
/// Ok(format!("{:?} {:?}", name, srcs))
/// }
/// }
/// ```
///
/// Parameters operate as named parameters of a given type, with six possible tweaks:
///
/// * `this` (or `_this`) as the first argument means the argument is passed as a
/// bound method value, e.g. in `a.f(...)` the `a` would be `this`.
/// * `args` means the argument is the `*args`.
/// * `kwargs` means the argument is the `**kwargs`.
/// * `ref name` means the argument must be passed by position, not by name.
/// * A type of `Option` means the argument is optional.
/// * A pattern `x @ foo : bool` means the argument defaults to `foo` if not
/// specified.
///
/// During execution there are two local variables injected into scope:
///
/// * `eval` is the `Evaluator`.
/// * `heap` is the `Heap`, obtained from `eval.heap()`.
///
/// A function with the `#[starlark_module]` attribute can be added to a `GlobalsBuilder` value
/// using the `with` function. Those `Globals` can be passed to `Evaluator` to provide global functions.
/// Alternatively, you can return `Globals` from `get_methods` to _attach_ functions to
/// a specific type (e.g. the `string` type).
///
/// * When unattached, you can define constants with `const`. We define `True`, `False` and
/// `None` that way.
/// * When attached, you can annotate the functions with `#[starlark(attribute)]` to turn the name into
/// an attribute on the value. Such a function must take exactly one argument, namely a value
/// of the type you have attached it to.
/// * The attribute `#[starlark(type("test"))]` causes `f.type` to return `"test"`.
/// * If a member is annotated with `#[starlark(speculative_exec_safe)]`, then a function
/// is considered safe to execute speculatively: the function should have
/// no global side effects, should not panic, and should finish in reasonable time.
/// The evaluator may invoke such functions early to generate more efficient code.
///
/// All these functions interoperate properly with `dir()`, `getattr()` and `hasattr()`.
///
/// If a desired function name is also a Rust keyword, use the `r#` prefix, e.g. `r#type`.
#[proc_macro_attribute]
pub fn starlark_module(attr: TokenStream, input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemFn);
fn starlark_module_impl(attr: TokenStream, input: ItemFn) -> syn::Result<TokenStream> {
assert!(attr.is_empty());
let mut x = parse::parse(input)?;
x.resolve()?;
Ok(render::render(x).into())
}
match starlark_module_impl(attr, input) {
Ok(x) => x,
Err(e) => e.to_compile_error().into(),
}
}
/// Stubs for Starlark bytecode interpreter.
#[proc_macro_attribute]
pub fn starlark_internal_bc(
attr: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
bc::starlark_internal_bc(attr, input)
}
/// Derive the `Trace` trait.
#[proc_macro_derive(Trace, attributes(trace))]
pub fn derive_trace(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
trace::derive_trace(input)
}
/// Derive the `Freeze` trait.
#[proc_macro_derive(Freeze, attributes(freeze))]
pub fn derive_freeze(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
freeze::derive_freeze(input)
}
/// Derive the `NoSerialize` trait for serde.
#[proc_macro_derive(NoSerialize)]
pub fn derive_no_serialize(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
serde::derive_no_serialize(input)
}
/// Derive accessor methods that are designed to be used from {has,get,dir}_attr
/// in an `impl StarlarkValue` block. All fields in the struct that are not
/// marked with #[starlark(skip)] are exported to Starlark code as attributes.
/// NOTE: Any usage must also call `starlark_attrs!()` in the impl block for
/// `StarlarkValue`, otherwise the generated attr methods will not be used.
#[proc_macro_derive(StarlarkAttrs, attributes(starlark))]
pub fn derive_starlark_attrs(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
attrs::derive_attrs(input)
}
/// Generate `{has,get,dir}_attr` in the `StarlarkValue` impl block that proxy
/// to the ones generated by `derive(StarlarkAttrs)`
#[proc_macro]
pub fn starlark_attrs(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
attrs::starlark_attrs()
}