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":"9011d1577db9a906ff2b62847293ac8c0992e13495cc175eb3a00d52893d4a90","src/ast_node_macro.rs":"9989e80bbc6506a04dee26bddd7c1d6a91ef73dfb20a8f3f38c0919e9bffa167","src/enum_deserialize.rs":"d6b78a6b8fdd2825ed5c4f73e38d4f0d6cb58a5e97e8f9d8aeca7dc5ae863e66","src/lib.rs":"770de4bd5e7df7c402ecc2226b17fa6520c0f7cca72c1702891801a6453a33a9","src/spanned.rs":"5142d4026fa5b415269e792fccb6907fe2e92de24b0b4c3dac8b1db32591a25f"},"package":"c09c69dffe06d222d072c878c3afe86eee2179806f20503faec97250268b4c24"}

47
third-party/vendor/ast_node/Cargo.toml vendored Normal file
View file

@ -0,0 +1,47 @@
# 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 = "ast_node"
version = "0.9.5"
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
description = "Macros for ast nodes."
documentation = "https://rustdoc.swc.rs/ast_node/"
license = "Apache-2.0"
repository = "https://github.com/swc-project/swc.git"
resolver = "1"
[lib]
bench = false
proc-macro = true
[dependencies.pmutil]
version = "0.6.1"
[dependencies.proc-macro2]
version = "1"
[dependencies.quote]
version = "1"
[dependencies.swc_macros_common]
version = "0.3.8"
[dependencies.syn]
version = "2"
features = [
"derive",
"fold",
"parsing",
"printing",
"visit-mut",
]

View file

@ -0,0 +1,171 @@
use pmutil::{smart_quote, Quote};
use swc_macros_common::prelude::*;
use syn::{
self,
parse::{Parse, ParseStream},
*,
};
#[derive(Clone)]
pub struct Args {
pub ty: Literal,
}
impl Parse for Args {
fn parse(i: ParseStream<'_>) -> syn::Result<Self> {
Ok(Args { ty: i.parse()? })
}
}
pub fn expand_struct(args: Args, i: DeriveInput) -> Vec<ItemImpl> {
let mut items = vec![];
let generics = i.generics.clone();
// let item_ident = Ident::new("Item", i.ident.span());
items.push(
Quote::new_call_site()
.quote_with(smart_quote!(
Vars {
Type: i.ident.clone(),
type_str: args.ty
},
{
impl ::swc_common::AstNode for Type {
const TYPE: &'static str = type_str;
}
}
))
.parse::<ItemImpl>()
.with_generics(generics),
);
// let ident = i.ident.clone();
// let cloned = i.clone();
// items.push({
// let (fields, item_data) = match i.data {
// Data::Struct(DataStruct {
// struct_token,
// semi_token,
// fields: Fields::Named(FieldsNamed { brace_token, named }),
// }) => {
// let fields: Punctuated<_, token::Comma> = named
// .clone()
// .into_iter()
// .map(|field| FieldValue {
// member: Member::Named(field.ident.clone().unwrap()),
// expr: Quote::new_call_site()
// .quote_with(smart_quote!(
// Vars {
// field: &field.ident
// },
// { node.node.field }
// ))
// .parse(),
// attrs: field
// .attrs
// .into_iter()
// .filter(|attr| is_attr_name(attr, "cfg"))
// .collect(),
// colon_token: Some(call_site()),
// })
// .collect();
// let item_data = Data::Struct(DataStruct {
// struct_token,
// semi_token,
// fields: Fields::Named(FieldsNamed {
// brace_token,
// named: named
// .into_pairs()
// .map(|pair| {
// let handle = |v: Field| Field {
// vis: Visibility::Inherited,
// attrs: v
// .attrs
// .into_iter()
// .filter(|attr| {
// is_attr_name(attr, "serde") ||
// is_attr_name(attr, "cfg") })
// .collect(),
// ..v
// };
// match pair {
// Pair::End(v) => Pair::End(handle(v)),
// Pair::Punctuated(v, p) =>
// Pair::Punctuated(handle(v), p), }
// })
// .collect(),
// }),
// });
// (fields, item_data)
// }
// _ => unreachable!("enum / tuple struct / union with
// #[ast_node(\"Foo\")]"), };
// let convert_item_to_self =
// Quote::new_call_site().quote_with(smart_quote!( Vars {
// fields,
// Type: &ident
// },
// { Type { fields } }
// ));
// let body = Quote::new_call_site().quote_with(smart_quote!(
// Vars {
// convert_item_to_self
// },
// {
// let node =
// ::swc_common::serializer::Node::<Item>::deserialize(deserializer)?;
// if node.ty != <Self as ::swc_common::AstNode>::TYPE {
// return Err(D::Error::unknown_variant(
// &node.ty,
// &[<Self as ::swc_common::AstNode>::TYPE],
// ));
// }
// Ok(convert_item_to_self)
// }
// ));
// let item = DeriveInput {
// vis: Visibility::Inherited,
// ident: item_ident,
// attrs: vec![],
// data: item_data,
// ..cloned
// };
// Quote::new_call_site()
// .quote_with(smart_quote!(
// Vars {
// // A new item which implements Deserialize
// item,
// Type: ident,
// body
// },
// {
// impl<'de> ::serde::Deserialize<'de> for Type {
// fn deserialize<D>(deserializer: D) -> Result<Self,
// D::Error> where
// D: ::serde::Deserializer<'de>,
// {
// use ::serde::de::Error;
// #[derive(::serde::Deserialize)]
// #[serde(rename_all = "camelCase")]
// ite
// body
// }
// }
// }
// ))
// .parse::<ItemImpl>()
// .with_generics(generics)
// });
items
}

View file

@ -0,0 +1,442 @@
use pmutil::{q, smart_quote, Quote, SpanExt};
use swc_macros_common::prelude::*;
use syn::{
self,
parse::{Parse, ParseStream},
*,
};
struct VariantAttr {
tags: Punctuated<Lit, Token![,]>,
}
impl Parse for VariantAttr {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(VariantAttr {
tags: input.call(Punctuated::parse_terminated)?,
})
}
}
pub fn expand(
DeriveInput {
generics,
ident,
data,
..
}: DeriveInput,
) -> ItemImpl {
let data = match data {
Data::Enum(data) => data,
_ => unreachable!("expand_enum is called with none-enum item"),
};
let mut has_wildcard = false;
let deserialize = {
let mut all_tags: Punctuated<_, token::Comma> = Default::default();
let tag_match_arms = data
.variants
.iter()
.map(|variant| {
let field_type = match variant.fields {
Fields::Unnamed(ref fields) => {
assert_eq!(
fields.unnamed.len(),
1,
"#[ast_node] enum cannot contain variant with multiple fields"
);
fields.unnamed.last().unwrap().ty.clone()
}
_ => {
unreachable!("#[ast_node] enum cannot contain named fields or unit variant")
}
};
let tags = variant
.attrs
.iter()
.filter_map(|attr| -> Option<VariantAttr> {
if !is_attr_name(attr, "tag") {
return None;
}
let tokens = match &attr.meta {
Meta::List(meta) => meta.tokens.clone(),
_ => {
panic!("#[tag] attribute must be in form of #[tag(..)]")
}
};
let tags = parse2(tokens).expect("failed to parse #[tag] attribute");
Some(tags)
})
.flat_map(|v| v.tags)
.collect::<Punctuated<_, token::Comma>>();
assert!(
!tags.is_empty(),
"All #[ast_node] enum variants have one or more tag"
);
// TODO: Clean up this code
if tags.len() == 1
&& match tags.first() {
Some(Lit::Str(s)) => &*s.value() == "*",
_ => false,
}
{
has_wildcard = true;
} else {
for tag in tags.iter() {
all_tags.push(tag.clone());
}
}
Arm {
attrs: Default::default(),
pat: Pat::Path(
q!(
Vars {
Variant: &variant.ident
},
(__TypeVariant::Variant)
)
.parse(),
),
guard: Default::default(),
fat_arrow_token: variant.ident.span().as_token(),
body: q!(
Vars {
Variant: &variant.ident,
FieldType: field_type,
},
{
swc_common::private::serde::Result::map(
<FieldType as serde::Deserialize>::deserialize(
swc_common::private::serde::de::ContentDeserializer::<
__D::Error,
>::new(__content),
),
Self::Variant,
)
}
)
.parse(),
comma: Some(variant.ident.span().as_token()),
}
})
.collect::<Vec<Arm>>();
let tag_expr = {
let mut visit_str_arms = vec![];
let mut visit_bytes_arms = vec![];
for variant in &data.variants {
let tags = variant
.attrs
.iter()
.filter_map(|attr| -> Option<VariantAttr> {
if !is_attr_name(attr, "tag") {
return None;
}
let tokens = match &attr.meta {
Meta::List(meta) => meta.tokens.clone(),
_ => {
panic!("#[tag] attribute must be in form of #[tag(..)]")
}
};
let tags = parse2(tokens).expect("failed to parse #[tag] attribute");
Some(tags)
})
.flat_map(|v| v.tags)
.collect::<Punctuated<_, token::Comma>>();
assert!(
!tags.is_empty(),
"All #[ast_node] enum variants have one or more tag"
);
let (str_pat, bytes_pat) = {
if tags.len() == 1
&& match tags.first() {
Some(Lit::Str(s)) => &*s.value() == "*",
_ => false,
}
{
(
Pat::Wild(PatWild {
attrs: Default::default(),
underscore_token: variant.ident.span().as_token(),
}),
Pat::Wild(PatWild {
attrs: Default::default(),
underscore_token: variant.ident.span().as_token(),
}),
)
} else {
fn make_pat(lit: Lit) -> (Pat, Pat) {
let s = match lit.clone() {
Lit::Str(s) => s.value(),
_ => {
unreachable!()
}
};
(
Pat::Lit(PatLit {
attrs: Default::default(),
lit,
}),
Pat::Lit(PatLit {
attrs: Default::default(),
lit: Lit::ByteStr(LitByteStr::new(s.as_bytes(), call_site())),
}),
)
}
if tags.len() == 1 {
make_pat(tags.first().unwrap().clone())
} else {
let mut str_cases = Punctuated::new();
let mut bytes_cases = Punctuated::new();
for tag in tags {
let (str_pat, bytes_pat) = make_pat(tag.clone());
str_cases.push(str_pat);
bytes_cases.push(bytes_pat);
}
(
Pat::Or(PatOr {
attrs: Default::default(),
leading_vert: Default::default(),
cases: str_cases,
}),
Pat::Or(PatOr {
attrs: Default::default(),
leading_vert: Default::default(),
cases: bytes_cases,
}),
)
}
}
};
visit_str_arms.push(Arm {
attrs: Default::default(),
pat: str_pat,
guard: None,
fat_arrow_token: variant.ident.span().as_token(),
body: q!(
Vars {
Variant: &variant.ident,
},
{ Ok(__TypeVariant::Variant) }
)
.parse(),
comma: Some(variant.ident.span().as_token()),
});
visit_bytes_arms.push(Arm {
attrs: Default::default(),
pat: bytes_pat,
guard: None,
fat_arrow_token: variant.ident.span().as_token(),
body: q!(
Vars {
Variant: &variant.ident,
},
{ Ok(__TypeVariant::Variant) }
)
.parse(),
comma: Some(variant.ident.span().as_token()),
});
}
if !has_wildcard {
visit_str_arms.push(Arm {
attrs: Default::default(),
pat: Pat::Wild(PatWild {
attrs: Default::default(),
underscore_token: ident.span().as_token(),
}),
guard: None,
fat_arrow_token: ident.span().as_token(),
body: q!({
swc_common::private::serde::Err(serde::de::Error::unknown_variant(
__value, VARIANTS,
))
})
.parse(),
comma: Some(ident.span().as_token()),
});
visit_bytes_arms.push(Arm {
attrs: Default::default(),
pat: Pat::Wild(PatWild {
attrs: Default::default(),
underscore_token: ident.span().as_token(),
}),
guard: None,
fat_arrow_token: ident.span().as_token(),
body: q!({
{
let __value = &swc_common::private::serde::from_utf8_lossy(__value);
swc_common::private::serde::Err(serde::de::Error::unknown_variant(
__value, VARIANTS,
))
}
})
.parse(),
comma: Some(ident.span().as_token()),
});
}
let visit_str_body = Expr::Match(ExprMatch {
attrs: Default::default(),
match_token: call_site(),
expr: q!((__value)).parse(),
brace_token: call_site(),
arms: visit_str_arms,
});
let visit_bytes_body = Expr::Match(ExprMatch {
attrs: Default::default(),
match_token: call_site(),
expr: q!((__value)).parse(),
brace_token: call_site(),
arms: visit_bytes_arms,
});
q!(
Vars {
visit_str_body,
visit_bytes_body,
all_tags: &all_tags,
},
{
{
static VARIANTS: &[&str] = &[all_tags];
struct __TypeVariantVisitor;
impl<'de> serde::de::Visitor<'de> for __TypeVariantVisitor {
type Value = __TypeVariant;
fn expecting(
&self,
__formatter: &mut swc_common::private::serde::Formatter,
) -> swc_common::private::serde::fmt::Result
{
swc_common::private::serde::Formatter::write_str(
__formatter,
"variant identifier",
)
}
fn visit_str<__E>(
self,
__value: &str,
) -> swc_common::private::serde::Result<Self::Value, __E>
where
__E: serde::de::Error,
{
visit_str_body
}
fn visit_bytes<__E>(
self,
__value: &[u8],
) -> swc_common::private::serde::Result<Self::Value, __E>
where
__E: serde::de::Error,
{
visit_bytes_body
}
}
impl<'de> serde::Deserialize<'de> for __TypeVariant {
#[inline]
fn deserialize<__D>(
__deserializer: __D,
) -> swc_common::private::serde::Result<Self, __D::Error>
where
__D: serde::Deserializer<'de>,
{
serde::Deserializer::deserialize_identifier(
__deserializer,
__TypeVariantVisitor,
)
}
}
let ty = swc_common::serializer::Type::deserialize(
swc_common::private::serde::de::ContentRefDeserializer::<__D::Error>::new(
&__content,
),
)?;
let __tagged = __TypeVariant::deserialize(
swc_common::private::serde::de::ContentDeserializer::<__D::Error>::new(
swc_common::private::serde::de::Content::Str(&ty.ty),
)
)?;
__tagged
}
}
)
.parse::<Expr>()
};
let match_type_expr = Expr::Match(ExprMatch {
attrs: Default::default(),
match_token: call_site(),
expr: q!({ __tagged }).parse(),
brace_token: call_site(),
arms: tag_match_arms,
});
let variants: Punctuated<Variant, Token![,]> = {
data.variants
.iter()
.cloned()
.map(|variant| Variant {
attrs: Default::default(),
fields: Fields::Unit,
..variant
})
.collect()
};
Quote::new_call_site()
.quote_with(smart_quote!(
Vars {
match_type_expr,
Enum: &ident,
tag_expr,
variants
},
{
#[cfg(feature = "serde-impl")]
impl<'de> serde::Deserialize<'de> for Enum {
#[allow(unreachable_code)]
fn deserialize<__D>(
__deserializer: __D,
) -> ::std::result::Result<Self, __D::Error>
where
__D: serde::Deserializer<'de>,
{
enum __TypeVariant {
variants,
}
let __content = <swc_common::private::serde::de::Content as serde::Deserialize>::deserialize(
__deserializer,
)?;
let __tagged = tag_expr;
match_type_expr
}
}
}
))
.parse::<ItemImpl>()
.with_generics(generics)
};
deserialize
}

308
third-party/vendor/ast_node/src/lib.rs vendored Normal file
View file

@ -0,0 +1,308 @@
#![deny(clippy::all)]
#![recursion_limit = "1024"]
extern crate proc_macro;
use pmutil::{smart_quote, Quote, ToTokensExt};
use swc_macros_common::prelude::*;
use syn::{self, visit_mut::VisitMut, *};
mod ast_node_macro;
mod enum_deserialize;
mod spanned;
/// Derives [`swc_common::Spanned`]. See [`swc_common::Spanned`] for
/// documentation.
#[proc_macro_derive(Spanned, attributes(span))]
pub fn derive_spanned(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse::<DeriveInput>(input).expect("failed to parse input as DeriveInput");
let item = self::spanned::derive(input);
print("derive(Spanned)", item.dump())
}
/// Derives `serde::Deserialize` which is aware of `tag` based deserialization.
#[proc_macro_derive(DeserializeEnum, attributes(tag))]
pub fn derive_deserialize_enum(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse::<DeriveInput>(input).expect("failed to parse input as DeriveInput");
let item = enum_deserialize::expand(input);
print("derive(DeserializeEnum)", item.dump())
}
/// Derives `serde::Serialize` and `serde::Deserialize`.
///
/// # Struct attributes
///
/// `#[ast_serde("A")]` adds `"type": "A"` to json when serialized, and
/// deserializes as the type only if `type` field of json string is `A`.
///
/// # Enum attributes
////
/// ## Type-level attributes
///
/// This macro does not accept arguments if used on enum.
///
/// ## Variant attributes
///
/// ### `#[tag("Expr")]`
///
/// You can tell "Use this variant if `type` is `Expr`".
///
/// This attribute can be applied multiple time, if a variant consumes multiple
/// `type`s.
///
/// For example, `Lit` of swc_ecma_ast is an enum, but `Expr`, which contains
/// `Lit` as a variant, is also an enum.
/// So the `Lit` variant has multiple `#[tag]`-s like
///
/// ```rust,ignore
/// enum Expr {
/// #[tag("StringLiteral")]
/// #[tag("NumericLiteral")]
/// #[tag("BooleanLiteral")]
/// Lit(Lit),
/// }
/// ```
///
/// so the deserializer can decide which variant to use.
///
///
/// `#[tag]` also supports wildcard like `#[tag("*")]`. You can use this if
/// there are two many variants.
#[proc_macro_attribute]
pub fn ast_serde(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let input: DeriveInput = parse(input).expect("failed to parse input as a DeriveInput");
// we should use call_site
let mut item = Quote::new(Span::call_site());
item = match input.data {
Data::Enum(..) => {
if !args.is_empty() {
panic!("#[ast_serde] on enum does not accept any argument")
}
item.quote_with(smart_quote!(Vars { input }, {
#[derive(::serde::Serialize, ::swc_common::DeserializeEnum)]
#[serde(untagged)]
input
}))
}
_ => {
let args: Option<ast_node_macro::Args> = if args.is_empty() {
None
} else {
Some(parse(args).expect("failed to parse args of #[ast_serde]"))
};
let serde_tag = match input.data {
Data::Struct(DataStruct {
fields: Fields::Named(..),
..
}) => {
if args.is_some() {
Some(Quote::new_call_site().quote_with(smart_quote!(Vars {}, {
#[serde(tag = "type")]
})))
} else {
None
}
}
_ => None,
};
let serde_rename = args.as_ref().map(|args| {
Quote::new_call_site().quote_with(smart_quote!(Vars { name: &args.ty },{
#[serde(rename = name)]
}))
});
item.quote_with(smart_quote!(Vars { input, serde_tag, serde_rename }, {
#[derive(::serde::Serialize, ::serde::Deserialize)]
serde_tag
#[serde(rename_all = "camelCase")]
serde_rename
input
}))
}
};
print("ast_serde", item.into())
}
struct AddAttr;
impl VisitMut for AddAttr {
fn visit_field_mut(&mut self, f: &mut Field) {
f.attrs
.push(parse_quote!(#[cfg_attr(feature = "__rkyv", omit_bounds)]));
}
}
/// Alias for
/// `#[derive(Spanned, Fold, Clone, Debug, PartialEq)]` for a struct and
/// `#[derive(Spanned, Fold, Clone, Debug, PartialEq, FromVariant)]` for an
/// enum.
#[proc_macro_attribute]
pub fn ast_node(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let mut input: DeriveInput = parse(input).expect("failed to parse input as a DeriveInput");
AddAttr.visit_data_mut(&mut input.data);
// we should use call_site
let mut item = Quote::new(Span::call_site());
item = match input.data {
Data::Enum(..) => {
struct EnumArgs {
clone: bool,
}
impl parse::Parse for EnumArgs {
fn parse(i: parse::ParseStream<'_>) -> syn::Result<Self> {
let name: Ident = i.parse()?;
if name != "no_clone" {
return Err(i.error("unknown attribute"));
}
Ok(EnumArgs { clone: false })
}
}
let args = if args.is_empty() {
EnumArgs { clone: true }
} else {
parse(args).expect("failed to parse args of #[ast_node]")
};
let clone = if args.clone {
Some(Quote::new_call_site().quote_with(smart_quote!(Vars {}, {
#[derive(Clone)]
})))
} else {
None
};
item.quote_with(smart_quote!(Vars { input, clone }, {
#[allow(clippy::derive_partial_eq_without_eq)]
#[cfg_attr(
feature = "serde-impl",
derive(
::serde::Serialize,
)
)]
#[derive(
::swc_common::FromVariant,
::swc_common::Spanned,
Debug,
PartialEq,
::swc_common::DeserializeEnum,
)]
clone
#[cfg_attr(
feature = "rkyv-impl",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(u32)))]
#[cfg_attr(
feature = "rkyv-impl",
archive(bound(
serialize = "__S: rkyv::ser::Serializer + rkyv::ser::ScratchSpace + rkyv::ser::SharedSerializeRegistry",
deserialize = "__D: rkyv::de::SharedDeserializeRegistry"
))
)]
#[cfg_attr(
feature = "serde-impl",
serde(untagged)
)]
input
}))
}
_ => {
let args: Option<ast_node_macro::Args> = if args.is_empty() {
None
} else {
Some(parse(args).expect("failed to parse args of #[ast_node]"))
};
let serde_tag = match input.data {
Data::Struct(DataStruct {
fields: Fields::Named(..),
..
}) => {
if args.is_some() {
Some(Quote::new_call_site().quote_with(smart_quote!(Vars {}, {
#[cfg_attr(
feature = "serde-impl",
serde(tag = "type")
)]
})))
} else {
None
}
}
_ => None,
};
let serde_rename = args.as_ref().map(|args| {
Quote::new_call_site().quote_with(smart_quote!(Vars { name: &args.ty }, {
#[cfg_attr(
feature = "serde-impl",
serde(rename = name)
)]
}))
});
let ast_node_impl = args
.as_ref()
.map(|args| ast_node_macro::expand_struct(args.clone(), input.clone()));
let mut quote =
item.quote_with(smart_quote!(Vars { input, serde_tag, serde_rename }, {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(::swc_common::Spanned, Clone, Debug, PartialEq)]
#[cfg_attr(
feature = "serde-impl",
derive(::serde::Serialize, ::serde::Deserialize)
)]
#[cfg_attr(
feature = "rkyv-impl",
derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
)]
#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))]
#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(C)))]
#[cfg_attr(
feature = "rkyv-impl",
archive(
bound(
serialize = "__S: rkyv::ser::Serializer + rkyv::ser::ScratchSpace + rkyv::ser::SharedSerializeRegistry",
deserialize = "__D: rkyv::de::SharedDeserializeRegistry"
)
)
)]
serde_tag
#[cfg_attr(
feature = "serde-impl",
serde(rename_all = "camelCase")
)]
serde_rename
input
}));
if let Some(items) = ast_node_impl {
for item in items {
quote.push_tokens(&item);
}
}
quote
}
};
print("ast_node", item.into())
}

View file

@ -0,0 +1,226 @@
#![allow(dead_code)]
use pmutil::{smart_quote, Quote, ToTokensExt};
use swc_macros_common::prelude::*;
use syn::{parse::Parse, *};
struct MyField {
/// Name of the field.
pub ident: Option<Ident>,
/// Type of the field.
pub ty: Type,
/// `#[span(lo)]`
pub lo: bool,
/// `#[span(hi)]`
pub hi: bool,
}
struct InputFieldAttr {
kinds: Punctuated<Ident, Token![,]>,
}
impl Parse for InputFieldAttr {
fn parse(input: parse::ParseStream) -> Result<Self> {
let kinds = input.call(Punctuated::parse_terminated)?;
Ok(Self { kinds })
}
}
impl MyField {
fn from_field(f: &Field) -> Self {
let mut lo = false;
let mut hi = false;
for attr in &f.attrs {
if !is_attr_name(attr, "span") {
continue;
}
match &attr.meta {
Meta::Path(..) => {}
Meta::List(list) => {
let input = parse2::<InputFieldAttr>(list.tokens.clone())
.expect("failed to parse as `InputFieldAttr`");
for kind in input.kinds {
if kind == "lo" {
lo = true
} else if kind == "hi" {
hi = true
} else {
panic!("Unknown span attribute: {:?}", kind)
}
}
}
_ => panic!("Unknown span attribute"),
}
}
Self {
ident: f.ident.clone(),
ty: f.ty.clone(),
lo,
hi,
}
}
}
pub fn derive(input: DeriveInput) -> ItemImpl {
let arms = Binder::new_from(&input)
.variants()
.into_iter()
.map(|v| {
let (pat, bindings) = v.bind("_", Some(def_site()), None);
let body = make_body_for_variant(&v, bindings);
Arm {
body,
attrs: v
.attrs()
.iter()
.filter(|attr| is_attr_name(attr, "cfg"))
.cloned()
.collect(),
pat,
guard: None,
fat_arrow_token: def_site(),
comma: Some(def_site()),
}
})
.collect();
let body = Expr::Match(ExprMatch {
attrs: Default::default(),
match_token: def_site(),
brace_token: def_site(),
expr: Box::new(
Quote::new(def_site::<Span>())
.quote_with(smart_quote!(Vars {}, { self }))
.parse(),
),
arms,
});
Quote::new(def_site::<Span>())
.quote_with(smart_quote!(
Vars {
Type: &input.ident,
body,
},
{
impl swc_common::Spanned for Type {
#[inline]
fn span(&self) -> swc_common::Span {
body
}
}
}
))
.parse::<ItemImpl>()
.with_generics(input.generics)
}
fn make_body_for_variant(v: &VariantBinder<'_>, bindings: Vec<BindedField<'_>>) -> Box<Expr> {
/// `swc_common::Spanned::span(#field)`
fn simple_field(field: &dyn ToTokens) -> Box<Expr> {
Box::new(
Quote::new(def_site::<Span>())
.quote_with(smart_quote!(Vars { field }, {
swc_common::Spanned::span(field)
}))
.parse(),
)
}
if bindings.is_empty() {
panic!("#[derive(Spanned)] requires a field to get span from")
}
if bindings.len() == 1 {
if let Fields::Unnamed(..) = *v.data() {
// Call self.0.span()
return simple_field(&bindings[0]);
}
}
// Handle #[span] attribute.
if let Some(f) = bindings
.iter()
.find(|b| has_empty_span_attr(&b.field().attrs))
{
//TODO: Verify that there's no more #[span]
return simple_field(f);
}
// If all fields do not have `#[span(..)]`, check for field named `span`.
let has_any_span_attr = bindings
.iter()
.map(|b| {
b.field()
.attrs
.iter()
.any(|attr| is_attr_name(attr, "span"))
})
.any(|b| b);
if !has_any_span_attr {
let span_field = bindings
.iter()
.find(|b| {
b.field()
.ident
.as_ref()
.map(|ident| ident == "span")
.unwrap_or(false)
})
.unwrap_or_else(|| {
panic!(
"#[derive(Spanned)]: cannot determine span field to use for {}",
v.qual_path().dump()
)
});
return simple_field(span_field);
}
let fields: Vec<_> = bindings
.iter()
.map(|b| (b, MyField::from_field(b.field())))
.collect();
// TODO: Only one field should be `#[span(lo)]`.
let lo = fields.iter().find(|&(_, f)| f.lo);
let hi = fields.iter().find(|&(_, f)| f.hi);
match (lo, hi) {
(Some((lo_field, _)), Some((hi_field, _))) => {
// Create a new span from lo_field.lo(), hi_field.hi()
Box::new(
Quote::new(def_site::<Span>())
.quote_with(smart_quote!(Vars { lo_field, hi_field }, {
swc_common::Spanned::span(lo_field)
.with_hi(swc_common::Spanned::span(hi_field).hi())
}))
.parse(),
)
}
_ => panic!("#[derive(Spanned)]: #[span(lo)] and #[span(hi)] is required"),
}
}
/// Search for `#[span]`
fn has_empty_span_attr(attrs: &[Attribute]) -> bool {
attrs.iter().any(|attr| {
if !is_attr_name(attr, "span") {
return false;
}
match &attr.meta {
Meta::Path(..) => true,
Meta::List(t) => t.tokens.is_empty(),
_ => false,
}
})
}