Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1 @@
{"files":{"Cargo.toml":"27da963292e1a6f42284beb6f3f484b9244fbc4e63d4647ccf13ed74eb5552cf","src/binder.rs":"6b0c34594bef5e61f6e9fc1d048ab389fe606dbbe577a1b9039a599ccc02f88f","src/derive.rs":"61d95d1cfe13c4c91f49b130771e5dfddca46d999ff75a4d784bbe39584f2eef","src/derive/generics.rs":"3333a2dbb3c3e95b4a0010666de8a951574a72a6d8e8ce90cf29944399016d11","src/lib.rs":"f654efe689a11157cbc97c5b6e679cb764459c9d883601283f540f11dfa975da","src/prelude.rs":"c5b2fde7a5e6b7feb425fff02fb0c6dd81c19c5308e5e83ae4d28dfd929b33fe","src/syn_ext.rs":"0f4f7736ea3c5549016cf1821137c488f6c033211a6febd656a90ab18186a62e"},"package":"7a273205ccb09b51fabe88c49f3b34c5a4631c4c00a16ae20e03111d6a42e832"}

View file

@ -0,0 +1,44 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
name = "swc_macros_common"
version = "0.3.8"
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
description = "Common utilities for swc macros."
documentation = "https://rustdoc.swc.rs/swc_macros_common/"
license = "Apache-2.0"
repository = "https://github.com/swc-project/swc.git"
resolver = "1"
[lib]
bench = false
[dependencies.pmutil]
version = "0.6.1"
[dependencies.proc-macro2]
version = "1"
[dependencies.quote]
version = "1"
[dependencies.syn]
version = "2"
features = [
"derive",
"visit",
"parsing",
"full",
"printing",
"extra-traits",
]

View file

@ -0,0 +1,306 @@
//! # Example
//!
//! `_binded_a`, `_binded_b` and `_binded_0` in below example are
//! `BindedField`.
//!
//! ```rust
//! struct S {
//! a: u8,
//! b: u16,
//! }
//! let s = S { a: 0, b: 0 };
//! match s {
//! S {
//! a: _binded_a,
//! b: _binded_b,
//! } => {}
//! }
//! enum E {
//! V1 { a: u8 },
//! V2(u16),
//! V3,
//! }
//! let e = E::V1 { a: 0 };
//! match e {
//! E::V1 { a: _binded_a } => {}
//! E::V2(_binded_0) => {}
//! E::V3 => {}
//! }
//! ```
//!
//!
//! -----
//!
//! Adopted from `synstructure`.
use pmutil::{prelude::*, *};
use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use syn::{
punctuated::Pair,
token::{Mut, Ref},
*,
};
use crate::{def_site, is_attr_name, syn_ext::PairExt};
/// Used to bind whole struct or enum.
#[derive(Debug, Clone)]
pub struct Binder<'a> {
ident: &'a Ident,
body: &'a Data,
attrs: &'a [Attribute],
}
impl<'a> Binder<'a> {
///
/// - `attrs`: Attributes of the type.
pub const fn new(ident: &'a Ident, body: &'a Data, attrs: &'a [Attribute]) -> Self {
Binder { ident, body, attrs }
}
pub fn new_from(input: &'a DeriveInput) -> Self {
Self::new(&input.ident, &input.data, &input.attrs)
}
///
pub fn variants(&self) -> Vec<VariantBinder<'a>> {
match *self.body {
Data::Enum(DataEnum { ref variants, .. }) => {
let enum_name = &self.ident;
variants
.iter()
.map(|v| VariantBinder::new(Some(enum_name), &v.ident, &v.fields, &v.attrs))
.collect()
}
Data::Struct(DataStruct { ref fields, .. }) => {
vec![VariantBinder::new(None, self.ident, fields, self.attrs)]
}
Data::Union(_) => unimplemented!("Binder for union type"),
}
}
}
/// Variant.
#[derive(Debug, Clone)]
pub struct VariantBinder<'a> {
/// None for struct.
enum_name: Option<&'a Ident>,
/// Name of variant.
name: &'a Ident,
data: &'a Fields,
attrs: &'a [Attribute],
}
impl<'a> VariantBinder<'a> {
pub const fn new(
enum_name: Option<&'a Ident>,
name: &'a Ident,
data: &'a Fields,
attrs: &'a [Attribute],
) -> Self {
VariantBinder {
enum_name,
name,
data,
attrs,
}
}
pub const fn variant_name(&self) -> &Ident {
self.name
}
pub const fn data(&self) -> &Fields {
self.data
}
pub const fn attrs(&self) -> &[Attribute] {
self.attrs
}
/// `EnumName::VariantName` for enum, and `StructName` for struct.
pub fn qual_path(&self) -> Path {
match self.enum_name {
Some(enum_name) => Quote::new(def_site::<Span>())
.quote_with(smart_quote!(
Vars {
EnumName: enum_name,
VariantName: self.name,
},
{ EnumName::VariantName }
))
.parse(),
None => self.name.clone().into(),
}
}
///
/// - `prefix`: prefix of field binding.
pub fn bind(
&self,
prefix: &str,
by_ref: Option<Ref>,
mutability: Option<Mut>,
) -> (Pat, Vec<BindedField<'a>>) {
let path = self.qual_path();
let (pat, bindings) = match *self.data {
Fields::Unit => {
// EnumName::VariantName
let pat = Pat::Path(PatPath {
qself: None,
path,
attrs: Default::default(),
});
// Unit struct does not have any field to bind
(pat, vec![])
}
Fields::Named(FieldsNamed {
named: ref fields,
brace_token,
}) => {
let mut bindings = vec![];
let fields = fields
.pairs()
.map(|e| {
let (t, p) = e.into_tuple();
Pair::new(t, p.cloned())
})
.enumerate()
.map(|(idx, f)| {
f.map_item(|f| {
let ident = f
.ident
.clone()
.expect("field of struct-like variants should have name");
let binded_ident = ident.new_ident_with(|s| format!("{}{}", prefix, s));
bindings.push(BindedField {
idx,
binded_ident: binded_ident.clone(),
field: f,
});
FieldPat {
attrs: f
.attrs
.iter()
.filter(|attr| is_attr_name(attr, "cfg"))
.cloned()
.collect(),
colon_token: f.colon_token,
member: Member::Named(ident),
pat: Box::new(Pat::Ident(PatIdent {
by_ref,
mutability,
ident: binded_ident,
subpat: None,
attrs: Default::default(),
})),
}
})
})
.collect();
// EnumName::VariantName { fields }
let pat = Pat::Struct(PatStruct {
attrs: Default::default(),
qself: None,
path,
brace_token,
fields,
rest: None,
});
(pat, bindings)
}
Fields::Unnamed(FieldsUnnamed {
unnamed: ref fields,
paren_token,
}) => {
// TODO
let mut bindings = vec![];
let pats = fields
.pairs()
.map(|e| {
let (t, p) = e.into_tuple();
Pair::new(t, p.cloned())
})
.enumerate()
.map(|(idx, f)| {
f.map_item(|f| {
let binded_ident =
def_site::<Span>().new_ident(format!("{}{}", prefix, idx));
bindings.push(BindedField {
idx,
binded_ident: binded_ident.clone(),
field: f,
});
Pat::Ident(PatIdent {
by_ref,
mutability,
ident: binded_ident,
subpat: None,
attrs: Default::default(),
})
})
})
.collect();
// EnumName::VariantName ( fields )
let pat = Pat::TupleStruct(PatTupleStruct {
attrs: Default::default(),
qself: None,
path,
paren_token,
elems: pats,
});
(pat, bindings)
}
};
// if we don't need to move fields, we should match on reference to make tuple
// work.
// let pat = match by_ref {
// Some(ref_token) => Pat::Ref(PatRef {
// pat: box pat,
// and_token: ref_token.0.as_token(),
// mutability,
// }),
// None => pat,
// };
(pat, bindings)
}
}
/// Binded field. Note that this struct acts like a binded variable for
/// `quote!`.
#[derive(Debug, Clone)]
pub struct BindedField<'a> {
binded_ident: Ident,
idx: usize,
field: &'a Field,
}
impl<'a> BindedField<'a> {
pub const fn idx(&self) -> usize {
self.idx
}
/// Name of field binding.
pub const fn name(&self) -> &Ident {
&self.binded_ident
}
pub const fn field(&self) -> &Field {
self.field
}
}
impl<'a> ToTokens for BindedField<'a> {
fn to_tokens(&self, t: &mut TokenStream) {
self.binded_ident.to_tokens(t)
}
}

View file

@ -0,0 +1,107 @@
use std::iter;
use pmutil::ToTokensExt;
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{punctuated::Pair, *};
use crate::def_site;
mod generics;
/// Generics of derived impl item.
#[derive(Debug, Clone)]
pub struct Derive<'a> {
input: &'a DeriveInput,
out: ItemImpl,
}
impl<'a> Derive<'a> {
pub fn new(input: &'a DeriveInput) -> Self {
let (generics, self_ty) = {
// Generics for impl cannot have default.
let params = input
.generics
.params
.clone()
.into_pairs()
.map(|mut pair| {
if let GenericParam::Type(ref mut t) = *pair.value_mut() {
t.eq_token = None;
t.default = None;
}
pair
})
.collect();
let generics = Generics {
params,
gt_token: input.generics.gt_token,
lt_token: input.generics.lt_token,
where_clause: input.generics.where_clause.clone(),
};
// Handle generic declared on type.
let ty: Box<Type> = {
let (_, ty_generics, _) = input.generics.split_for_impl();
let mut t = TokenStream::new();
input.ident.to_tokens(&mut t);
ty_generics.to_tokens(&mut t);
Box::new(parse(t.dump().into()).unwrap_or_else(|err| {
panic!("failed to parse type: {}\nType: {}", err, t.dump())
}))
};
(generics, ty)
};
Derive {
input,
out: ItemImpl {
attrs: vec![],
impl_token: def_site(),
brace_token: def_site(),
defaultness: None,
unsafety: None,
generics,
trait_: None,
self_ty,
items: Default::default(),
},
}
}
/// Set `defaultness`
pub fn defaultness(&mut self, defaultness: Option<token::Default>) {
self.out.defaultness = defaultness;
}
/// Set `unsafety`
pub fn unsafety(&mut self, unsafety: Option<token::Unsafe>) {
self.out.unsafety = unsafety;
}
pub fn input(&self) -> &DeriveInput {
self.input
}
pub fn append_to(mut self, item: ItemImpl) -> ItemImpl {
assert_eq!(self.out.trait_, None);
if !self.out.generics.params.empty_or_trailing() {
self.out.generics.params.push_punct(def_site());
}
self.out
.generics
.params
.extend(item.generics.params.into_pairs());
self.out.trait_ = item.trait_;
self.out.attrs.extend(item.attrs);
self.out.items.extend(item.items);
self.out
}
}

View file

@ -0,0 +1,122 @@
use std::collections::BTreeSet;
use quote::quote;
use syn::visit::{self, Visit};
use super::*;
impl<'a> Derive<'a> {
pub fn all_generic_fields(&self) -> Vec<&'a Field> {
struct TypeVisitor<'a> {
params: &'a BTreeSet<Ident>,
is_generic: bool,
}
impl<'a, 'b> Visit<'a> for TypeVisitor<'b> {
fn visit_path(&mut self, path: &Path) {
if let Some(seg) = path.segments.last() {
if seg.ident == "PhantomData" {
// Hardcoded exception.
// This assumes name of the associated type is not PhantomData.
return;
}
}
if path.leading_colon.is_none() {
if let Some(seg) = path.segments.first() {
let id = &seg.ident;
if self.params.contains(id) {
self.is_generic = true;
}
}
}
visit::visit_path(self, path)
}
fn visit_macro(&mut self, _: &Macro) {}
}
struct FieldVisitor<'a> {
/// Type parameters defined on type.
params: BTreeSet<Ident>,
fields: Vec<&'a Field>,
}
impl<'a: 'b, 'b> Visit<'a> for FieldVisitor<'b> {
fn visit_field(&mut self, field: &'a Field) {
let mut vis = TypeVisitor {
params: &self.params,
is_generic: false,
};
vis.visit_type(&field.ty);
if vis.is_generic {
self.fields.push(field);
}
}
}
let mut vis = FieldVisitor {
params: self
.input
.generics
.params
.iter()
.filter_map(|p| match *p {
GenericParam::Type(TypeParam { ref ident, .. }) => Some(ident.clone()),
_ => None,
})
.collect(),
fields: vec![],
};
vis.visit_derive_input(self.input);
vis.fields
}
pub fn add_where_predicates<I>(&mut self, preds: I)
where
I: IntoIterator<Item = WherePredicate>,
{
let preds = preds.into_iter().map(|t| Pair::Punctuated(t, def_site()));
match self.out.generics.where_clause {
Some(WhereClause {
ref mut predicates, ..
}) => {
if !predicates.empty_or_trailing() {
predicates.push_punct(def_site());
}
predicates.extend(preds)
}
None => {
self.out.generics.where_clause = Some(WhereClause {
where_token: def_site(),
predicates: preds.collect(),
})
}
}
}
/// Add `Self: #trait_`.
pub fn bound_self(&mut self, trait_: Path) {
let self_ty: Type = parse(quote!(Self).into()).unwrap();
let bound = WherePredicate::Type(PredicateType {
lifetimes: None,
bounded_ty: self_ty,
colon_token: def_site(),
// `Trait` in `Self: Trait`
bounds: iter::once(Pair::End(TypeParamBound::Trait(TraitBound {
modifier: TraitBoundModifier::None,
lifetimes: None,
path: trait_,
paren_token: None,
})))
.collect(),
});
self.add_where_predicates(iter::once(bound))
}
}

View file

@ -0,0 +1,126 @@
//! Internal crate for the swc project.
extern crate proc_macro;
#[cfg(procmacro2_semver_exempt)]
use pmutil::SpanExt;
use pmutil::{prelude::*, synom_ext::FromSpan, Quote, SpanExt};
use proc_macro2::Span;
use quote::ToTokens;
use syn::*;
pub mod binder;
pub mod derive;
pub mod prelude;
mod syn_ext;
pub fn call_site<T: FromSpan>() -> T {
T::from_span(Span::call_site())
}
/// `Span::def_site().located_at(Span::call_site()).as_token()`
#[cfg(not(procmacro2_semver_exempt))]
pub fn def_site<T: FromSpan>() -> T {
call_site()
}
/// `Span::def_site().located_at(Span::call_site()).as_token()`
#[cfg(procmacro2_semver_exempt)]
pub fn def_site<T: FromSpan>() -> T {
Span::def_site().located_at(Span::call_site()).as_token()
}
/// `attr` - tokens inside `#[]`. e.g. `derive(EqIgnoreSpan)`, ast_node
pub fn print(attr: &'static str, tokens: proc_macro2::TokenStream) -> proc_macro::TokenStream {
use std::env;
match env::var("PRINT_GENERATED") {
Ok(ref s) if s == "1" || attr == s => {}
_ => return tokens.into(),
}
println!("\n\tOutput of #[{}]:\n\t {}", attr, tokens);
tokens.into()
}
pub fn is_attr_name(attr: &Attribute, name: &str) -> bool {
attr.path().is_ident(name)
}
/// Returns `None` if `attr` is not a doc attribute.
pub fn doc_str(attr: &Attribute) -> Option<String> {
fn parse_tts(attr: &Attribute) -> String {
match &attr.meta {
Meta::NameValue(MetaNameValue {
value:
Expr::Lit(ExprLit {
lit: Lit::Str(s), ..
}),
..
}) => s.value(),
_ => panic!("failed to parse {:?}", attr.meta),
}
}
if !is_attr_name(attr, "doc") {
return None;
}
Some(parse_tts(attr))
}
/// Creates a doc comment.
pub fn make_doc_attr(s: &str) -> Attribute {
comment(s)
}
pub fn access_field(obj: &dyn ToTokens, idx: usize, f: &Field) -> Expr {
Expr::Field(ExprField {
attrs: Default::default(),
base: syn::parse2(obj.to_token_stream())
.expect("swc_macros_common::access_field: failed to parse object"),
dot_token: Span::call_site().as_token(),
member: match &f.ident {
Some(id) => Member::Named(id.clone()),
_ => Member::Unnamed(Index {
index: idx as _,
span: Span::call_site(),
}),
},
})
}
pub fn join_stmts(stmts: &[Stmt]) -> Quote {
let mut q = Quote::new_call_site();
for s in stmts {
q.push_tokens(s);
}
q
}
/// fail! is a panic! with location reporting.
#[macro_export]
macro_rules! fail {
($($args:tt)+) => {{
panic!("{}\n --> {}:{}:{}", format_args!($($args)*), file!(), line!(), column!());
}};
}
#[macro_export]
macro_rules! unimplemented {
($($args:tt)+) => {{
fail!("not yet implemented: {}", format_args!($($args)*));
}};
}
#[macro_export]
macro_rules! unreachable {
() => {{
fail!("internal error: unreachable");
}};
($($args:tt)+) => {{
fail!("internal error: unreachable\n{}", format_args!($($args)*));
}};
}

View file

@ -0,0 +1,11 @@
pub use proc_macro2::{Delimiter, Group, Literal, Punct, Span, TokenStream, TokenTree};
pub use quote::ToTokens;
pub use syn::punctuated::{Pair as Element, Punctuated};
pub use super::{
binder::{BindedField, Binder, VariantBinder},
call_site, def_site,
derive::Derive,
doc_str, is_attr_name, print,
syn_ext::{ItemImplExt, PairExt},
};

View file

@ -0,0 +1,95 @@
use quote::quote;
use syn::{punctuated::Pair, *};
use crate::def_site;
/// Extension trait for `ItemImpl` (impl block).
pub trait ItemImplExt {
/// Instead of
///
/// ```rust,ignore
/// let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
///
/// let item: Item = Quote::new(def_site::<Span>())
/// .quote_with(smart_quote!(
/// Vars {
/// Type: type_name,
/// impl_generics,
/// ty_generics,
/// where_clause,
/// },
/// {
/// impl impl_generics ::swc_common::AstNode for Type ty_generics
/// where_clause {}
/// }
/// )).parse();
/// ```
///
/// You can use this like
///
/// ```rust,ignore
// let item = Quote::new(def_site::<Span>())
/// .quote_with(smart_quote!(Vars { Type: type_name }, {
/// impl ::swc_common::AstNode for Type {}
/// }))
/// .parse::<ItemImpl>()
/// .with_generics(input.generics);
/// ```
fn with_generics(self, generics: Generics) -> Self;
}
impl ItemImplExt for ItemImpl {
fn with_generics(mut self, mut generics: Generics) -> Self {
// TODO: Check conflicting name
let need_new_punct = !generics.params.empty_or_trailing();
if need_new_punct {
generics.params.push_punct(def_site());
}
// Respan
if let Some(t) = generics.lt_token {
self.generics.lt_token = Some(t)
}
if let Some(t) = generics.gt_token {
self.generics.gt_token = Some(t)
}
let ty = self.self_ty;
// Handle generics defined on struct, enum, or union.
let mut item: ItemImpl = {
let (_, ty_generics, _) = generics.split_for_impl();
let item = quote! {
#ty #ty_generics
};
ItemImpl {
generics,
self_ty: parse2(item).unwrap(),
..self
}
};
// Handle generics added by proc-macro.
item.generics
.params
.extend(self.generics.params.into_pairs());
item
}
}
pub trait PairExt<T, P>: Sized + Into<Pair<T, P>> {
fn map_item<F, NewItem>(self, op: F) -> Pair<NewItem, P>
where
F: FnOnce(T) -> NewItem,
{
match self.into() {
Pair::Punctuated(t, p) => Pair::Punctuated(op(t), p),
Pair::End(t) => Pair::End(op(t)),
}
}
}
impl<T, P> PairExt<T, P> for Pair<T, P> {}