mirror of
https://github.com/swc-project/swc.git
synced 2024-11-23 00:32:15 +03:00
feat(es/quote): Implement quasi quoter partially (#3155)
This commit is contained in:
parent
cc2b753895
commit
fe0ddcc54b
26
Cargo.lock
generated
26
Cargo.lock
generated
@ -3109,6 +3109,32 @@ dependencies = [
|
||||
"testing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_quote"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"swc_atoms",
|
||||
"swc_common",
|
||||
"swc_ecma_ast",
|
||||
"swc_ecma_quote_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_quote_macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"pmutil",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"swc_atoms",
|
||||
"swc_common",
|
||||
"swc_ecma_ast",
|
||||
"swc_ecma_parser",
|
||||
"swc_macros_common",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_transforms"
|
||||
version = "0.124.0"
|
||||
|
@ -6,6 +6,7 @@ members = [
|
||||
"crates/swc_css",
|
||||
"crates/swc_ecmascript",
|
||||
"crates/swc_ecma_lints",
|
||||
"crates/swc_ecma_quote",
|
||||
"crates/swc_estree_compat",
|
||||
"crates/swc_plugin",
|
||||
"crates/swc_plugin_macro",
|
||||
|
@ -12,9 +12,6 @@ version = "0.17.9"
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["lib", "dylib"]
|
||||
|
||||
[features]
|
||||
concurrent = ["parking_lot"]
|
||||
debug = []
|
||||
|
@ -238,12 +238,10 @@ impl<'a> Visit for DependencyCollector<'a> {
|
||||
|
||||
let kind = if node.is_type_only {
|
||||
DependencyKind::ImportType
|
||||
} else if node.is_export {
|
||||
DependencyKind::ExportEquals
|
||||
} else {
|
||||
if node.is_export {
|
||||
DependencyKind::ExportEquals
|
||||
} else {
|
||||
DependencyKind::ImportEquals
|
||||
}
|
||||
DependencyKind::ImportEquals
|
||||
};
|
||||
|
||||
self.items.push(DependencyDescriptor {
|
||||
|
@ -11,6 +11,10 @@ use crate::{
|
||||
};
|
||||
|
||||
impl<'a, I: Tokens> Parser<I> {
|
||||
pub fn parse_pat(&mut self) -> PResult<Pat> {
|
||||
self.parse_binding_pat_or_ident()
|
||||
}
|
||||
|
||||
pub(super) fn parse_opt_binding_ident(&mut self) -> PResult<Option<BindingIdent>> {
|
||||
trace_cur!(self, parse_opt_binding_ident);
|
||||
|
||||
|
@ -8,6 +8,10 @@ use crate::error::SyntaxError;
|
||||
mod module_item;
|
||||
|
||||
impl<'a, I: Tokens> Parser<I> {
|
||||
pub fn parse_module_item(&mut self) -> PResult<ModuleItem> {
|
||||
self.parse_stmt_like(true, true)
|
||||
}
|
||||
|
||||
pub(super) fn parse_block_body<Type>(
|
||||
&mut self,
|
||||
mut allow_directives: bool,
|
||||
@ -2171,13 +2175,13 @@ export default function waitUntil(callback, options = {}) {
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "Trailing comma is disallowed inside import(...) arguments")]
|
||||
fn error_for_trailing_commma_inside_dynamic_import() {
|
||||
fn error_for_trailing_comma_inside_dynamic_import() {
|
||||
let src = "import('foo',)";
|
||||
test_parser(src, Syntax::Es(Default::default()), |p| p.parse_expr());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_error_for_trailing_commma_inside_dynamic_import_with_import_assertions() {
|
||||
fn no_error_for_trailing_comma_inside_dynamic_import_with_import_assertions() {
|
||||
let src = "import('foo',)";
|
||||
test_parser(
|
||||
src,
|
||||
|
17
crates/swc_ecma_quote/Cargo.toml
Normal file
17
crates/swc_ecma_quote/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
description = "Quasi quotation system for ecmascript"
|
||||
documentation = "https://rustdoc.swc.rs/swc_ecma_quote/"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
name = "swc_ecma_quote"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.1.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
swc_atoms = {version = "0.2.9", path = "../swc_atoms"}
|
||||
swc_common = {version = "0.17.9", path = "../swc_common"}
|
||||
swc_ecma_ast = {version = "0.68.3", path = "../swc_ecma_ast"}
|
||||
swc_ecma_quote_macros = {version = "0.1.0", path = "../swc_ecma_quote_macros"}
|
68
crates/swc_ecma_quote/src/lib.rs
Normal file
68
crates/swc_ecma_quote/src/lib.rs
Normal file
@ -0,0 +1,68 @@
|
||||
/// Not a public API.
|
||||
#[doc(hidden)]
|
||||
pub extern crate swc_common;
|
||||
/// Not a public API.
|
||||
#[doc(hidden)]
|
||||
pub extern crate swc_ecma_ast;
|
||||
/// Not a public API.
|
||||
#[doc(hidden)]
|
||||
pub extern crate swc_ecma_quote_macros;
|
||||
|
||||
/// # Supported output types
|
||||
///
|
||||
/// - `Expr`
|
||||
/// - `Pat`
|
||||
/// - `Stmt`
|
||||
/// - `ModuleItem`
|
||||
///
|
||||
/// - Option<T> where T is supported type
|
||||
/// - Box<T> where T is supported type
|
||||
///
|
||||
/// For example, `Box<Expr>` and `Option<Box<Expr>>` are supported.
|
||||
///
|
||||
/// # Variable substitution
|
||||
///
|
||||
/// (**Not implemented**)
|
||||
///
|
||||
/// If an identifier starts with `$`, it is substituted with the value of the
|
||||
/// parameter passed.
|
||||
///
|
||||
/// e.g.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// quote!("const $name = 4;" as Stmt, name = private_ident!("ref"))
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Quote a variable declaration
|
||||
///
|
||||
/// (**Not implemented**)
|
||||
///
|
||||
/// ```rust
|
||||
/// use swc_ecma_ast::*;
|
||||
/// use swc_ecma_quote::quote;
|
||||
///
|
||||
/// let stmt = quote!("const $name = 4;" as Stmt, name = private_ident!("ref"));
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! quote {
|
||||
($($tt:tt)*) => {{
|
||||
$crate::swc_ecma_quote_macros::internal_quote!($($tt)*)
|
||||
}};
|
||||
}
|
||||
|
||||
/// Creates a `Box<Expr>` from the source code.
|
||||
///
|
||||
/// This is an alias for [quote], but without `as Box<Expr>`.
|
||||
#[macro_export]
|
||||
macro_rules! quote_expr {
|
||||
($($tt1:tt)*) => {{
|
||||
$crate::quote!($($tt1)* as Box<Expr>)
|
||||
}};
|
||||
|
||||
($($tt1:tt)*, $($tt2:tt)*) => {{
|
||||
$crate::quote!($($tt1)* as Box<Expr>, $($tt2)*)
|
||||
}};
|
||||
}
|
6
crates/swc_ecma_quote/tests/simple.rs
Normal file
6
crates/swc_ecma_quote/tests/simple.rs
Normal file
@ -0,0 +1,6 @@
|
||||
use swc_ecma_quote::quote_expr;
|
||||
|
||||
#[test]
|
||||
fn quote_expr_call_1() {
|
||||
let _expr = quote_expr!("call(arg1, typeof arg2, arg3)");
|
||||
}
|
26
crates/swc_ecma_quote_macros/Cargo.toml
Normal file
26
crates/swc_ecma_quote_macros/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
description = "Quasi quotation system for ecmascript"
|
||||
documentation = "https://rustdoc.swc.rs/swc_ecma_quote_macros/"
|
||||
edition = "2021"
|
||||
license = "Apache-2.0"
|
||||
name = "swc_ecma_quote_macros"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.1.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
pmutil = "0.5.1"
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
swc_atoms = {version = "0.2.9", path = "../swc_atoms"}
|
||||
swc_common = {version = "0.17.9", path = "../swc_common"}
|
||||
swc_ecma_ast = {version = "0.68.3", path = "../swc_ecma_ast"}
|
||||
swc_ecma_parser = {version = "0.91.1", path = "../swc_ecma_parser"}
|
||||
swc_macros_common = {version = "0.3.3", path = "../swc_macros_common"}
|
||||
syn = "1"
|
71
crates/swc_ecma_quote_macros/src/ast/class.rs
Normal file
71
crates/swc_ecma_quote_macros/src/ast/class.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
impl_struct!(
|
||||
Class,
|
||||
[
|
||||
span,
|
||||
decorators,
|
||||
body,
|
||||
super_class,
|
||||
is_abstract,
|
||||
type_params,
|
||||
super_type_params,
|
||||
implements
|
||||
]
|
||||
);
|
||||
|
||||
impl_struct!(
|
||||
Constructor,
|
||||
[span, key, params, body, accessibility, is_optional]
|
||||
);
|
||||
|
||||
impl_struct!(
|
||||
ClassMethod,
|
||||
[
|
||||
span,
|
||||
key,
|
||||
function,
|
||||
kind,
|
||||
is_static,
|
||||
accessibility,
|
||||
is_abstract,
|
||||
is_optional,
|
||||
is_override
|
||||
]
|
||||
);
|
||||
|
||||
impl_struct!(
|
||||
PrivateMethod,
|
||||
[
|
||||
span,
|
||||
key,
|
||||
function,
|
||||
kind,
|
||||
is_static,
|
||||
accessibility,
|
||||
is_abstract,
|
||||
is_optional,
|
||||
is_override
|
||||
]
|
||||
);
|
||||
|
||||
impl_struct!(
|
||||
ClassProp,
|
||||
[
|
||||
span,
|
||||
key,
|
||||
value,
|
||||
type_ann,
|
||||
is_static,
|
||||
decorators,
|
||||
accessibility,
|
||||
is_abstract,
|
||||
is_optional,
|
||||
is_override,
|
||||
readonly,
|
||||
declare,
|
||||
definite
|
||||
]
|
||||
);
|
||||
|
||||
impl_struct!(StaticBlock, [span, body]);
|
11
crates/swc_ecma_quote_macros/src/ast/decl.rs
Normal file
11
crates/swc_ecma_quote_macros/src/ast/decl.rs
Normal file
@ -0,0 +1,11 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
impl_enum!(
|
||||
Decl,
|
||||
[Class, Fn, Var, TsInterface, TsTypeAlias, TsEnum, TsModule]
|
||||
);
|
||||
|
||||
impl_struct!(ClassDecl, [ident, class]);
|
||||
impl_struct!(FnDecl, [ident, function]);
|
||||
impl_struct!(VarDecl, [span, kind, declare, decls]);
|
||||
impl_struct!(VarDeclarator, [span, name, init, definite]);
|
79
crates/swc_ecma_quote_macros/src/ast/enums.rs
Normal file
79
crates/swc_ecma_quote_macros/src/ast/enums.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use pmutil::q;
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
macro_rules! impl_simple_enum {
|
||||
($E:ident, [ $($v:ident),* ]) => {
|
||||
impl crate::ast::ToCode for $E {
|
||||
fn to_code(&self, _: &crate::ctxt::Ctx) -> syn::Expr {
|
||||
match self {
|
||||
$(
|
||||
$E::$v => q!(
|
||||
Vars {},
|
||||
{ swc_ecma_ast::$E::$v }
|
||||
)
|
||||
.parse(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_simple_enum!(VarDeclKind, [Var, Const, Let]);
|
||||
impl_simple_enum!(UnaryOp, [Minus, Plus, Bang, Tilde, TypeOf, Void, Delete]);
|
||||
impl_simple_enum!(UpdateOp, [PlusPlus, MinusMinus]);
|
||||
impl_simple_enum!(
|
||||
AssignOp,
|
||||
[
|
||||
Assign,
|
||||
AddAssign,
|
||||
SubAssign,
|
||||
MulAssign,
|
||||
DivAssign,
|
||||
ModAssign,
|
||||
LShiftAssign,
|
||||
RShiftAssign,
|
||||
ZeroFillRShiftAssign,
|
||||
BitOrAssign,
|
||||
BitXorAssign,
|
||||
BitAndAssign,
|
||||
ExpAssign,
|
||||
AndAssign,
|
||||
OrAssign,
|
||||
NullishAssign
|
||||
]
|
||||
);
|
||||
impl_simple_enum!(
|
||||
BinaryOp,
|
||||
[
|
||||
EqEq,
|
||||
NotEq,
|
||||
EqEqEq,
|
||||
NotEqEq,
|
||||
Lt,
|
||||
LtEq,
|
||||
Gt,
|
||||
GtEq,
|
||||
LShift,
|
||||
RShift,
|
||||
ZeroFillRShift,
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Mod,
|
||||
BitOr,
|
||||
BitXor,
|
||||
BitAnd,
|
||||
LogicalOr,
|
||||
LogicalAnd,
|
||||
In,
|
||||
InstanceOf,
|
||||
Exp,
|
||||
NullishCoalescing
|
||||
]
|
||||
);
|
||||
|
||||
impl_simple_enum!(Accessibility, [Public, Protected, Private]);
|
||||
impl_simple_enum!(MethodKind, [Method, Getter, Setter]);
|
||||
impl_simple_enum!(MetaPropKind, [NewTarget, ImportMeta]);
|
136
crates/swc_ecma_quote_macros/src/ast/expr.rs
Normal file
136
crates/swc_ecma_quote_macros/src/ast/expr.rs
Normal file
@ -0,0 +1,136 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
impl_enum!(PatOrExpr, [Pat, Expr]);
|
||||
impl_enum!(
|
||||
Expr,
|
||||
[
|
||||
This,
|
||||
Array,
|
||||
Object,
|
||||
Fn,
|
||||
Unary,
|
||||
Update,
|
||||
Bin,
|
||||
Assign,
|
||||
Member,
|
||||
SuperProp,
|
||||
Cond,
|
||||
Call,
|
||||
New,
|
||||
Seq,
|
||||
Ident,
|
||||
Lit,
|
||||
Tpl,
|
||||
TaggedTpl,
|
||||
Arrow,
|
||||
Class,
|
||||
Yield,
|
||||
MetaProp,
|
||||
Await,
|
||||
Paren,
|
||||
JSXMember,
|
||||
JSXNamespacedName,
|
||||
JSXEmpty,
|
||||
JSXElement,
|
||||
JSXFragment,
|
||||
TsTypeAssertion,
|
||||
TsConstAssertion,
|
||||
TsNonNull,
|
||||
TsAs,
|
||||
TsInstantiation,
|
||||
PrivateName,
|
||||
OptChain,
|
||||
Invalid
|
||||
]
|
||||
);
|
||||
|
||||
impl_struct!(ThisExpr, [span]);
|
||||
impl_struct!(ArrayLit, [span, elems]);
|
||||
impl_struct!(ObjectLit, [span, props]);
|
||||
impl_struct!(FnExpr, [ident, function]);
|
||||
impl_struct!(
|
||||
ArrowExpr,
|
||||
[
|
||||
span,
|
||||
params,
|
||||
body,
|
||||
is_async,
|
||||
is_generator,
|
||||
type_params,
|
||||
return_type
|
||||
]
|
||||
);
|
||||
impl_struct!(ClassExpr, [ident, class]);
|
||||
impl_struct!(Tpl, [exprs, quasis]);
|
||||
impl_struct!(UnaryExpr, [span, op, arg]);
|
||||
impl_struct!(UpdateExpr, [span, op, prefix, arg]);
|
||||
impl_struct!(BinExpr, [span, op, left, right]);
|
||||
impl_struct!(AssignExpr, [span, op, left, right]);
|
||||
impl_struct!(MemberExpr, [span, obj, prop]);
|
||||
impl_struct!(SuperPropExpr, [span, obj, prop]);
|
||||
impl_struct!(CondExpr, [span, test, cons, alt]);
|
||||
|
||||
impl_struct!(CallExpr, [span, callee, args, type_args]);
|
||||
impl_struct!(ExprOrSpread, [spread, expr]);
|
||||
impl_struct!(Super, [span]);
|
||||
impl_struct!(Import, [span]);
|
||||
impl_struct!(NewExpr, [span, callee, args, type_args]);
|
||||
impl_struct!(SeqExpr, [span, exprs]);
|
||||
|
||||
impl_struct!(TaggedTpl, [span, tag, type_params, tpl]);
|
||||
impl_struct!(YieldExpr, [span, arg, delegate]);
|
||||
impl_struct!(MetaPropExpr, [span, kind]);
|
||||
impl_struct!(AwaitExpr, [span, arg]);
|
||||
impl_struct!(JSXMemberExpr, [obj, prop]);
|
||||
impl_struct!(JSXNamespacedName, [ns, name]);
|
||||
impl_struct!(JSXEmptyExpr, [span]);
|
||||
impl_struct!(JSXElement, [span, opening, closing, children]);
|
||||
impl_struct!(JSXFragment, [span, opening, closing, children]);
|
||||
impl_struct!(OptChainExpr, [span, question_dot_token, base]);
|
||||
|
||||
impl_struct!(ParenExpr, [span, expr]);
|
||||
impl_struct!(
|
||||
Function,
|
||||
[
|
||||
params,
|
||||
decorators,
|
||||
span,
|
||||
body,
|
||||
is_generator,
|
||||
is_async,
|
||||
type_params,
|
||||
return_type
|
||||
]
|
||||
);
|
||||
impl_struct!(Decorator, [span, expr]);
|
||||
|
||||
impl_struct!(TplElement, [span, tail, cooked, raw]);
|
||||
|
||||
impl_struct!(
|
||||
JSXOpeningElement,
|
||||
[name, span, attrs, self_closing, type_args]
|
||||
);
|
||||
impl_struct!(JSXClosingElement, [name, span]);
|
||||
|
||||
impl_struct!(JSXOpeningFragment, [span]);
|
||||
impl_struct!(JSXClosingFragment, [span]);
|
||||
|
||||
impl_struct!(SpreadElement, [dot3_token, expr]);
|
||||
|
||||
impl_struct!(JSXExprContainer, [span, expr]);
|
||||
impl_struct!(JSXSpreadChild, [span, expr]);
|
||||
|
||||
impl_struct!(JSXAttr, [span, name, value]);
|
||||
|
||||
impl_enum!(
|
||||
JSXAttrValue,
|
||||
[Lit, JSXExprContainer, JSXElement, JSXFragment]
|
||||
);
|
||||
|
||||
impl_enum!(JSXAttrName, [Ident, JSXNamespacedName]);
|
||||
|
||||
impl_enum!(JSXExpr, [Expr, JSXEmptyExpr]);
|
||||
|
||||
impl_struct!(OptCall, [span, callee, args, type_args]);
|
||||
|
||||
impl_enum!(Callee, [Super, Import, Expr]);
|
26
crates/swc_ecma_quote_macros/src/ast/id.rs
Normal file
26
crates/swc_ecma_quote_macros/src/ast/id.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use pmutil::q;
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
use super::ToCode;
|
||||
use crate::ctxt::Ctx;
|
||||
|
||||
impl ToCode for swc_ecma_ast::Ident {
|
||||
fn to_code(&self, cx: &Ctx) -> syn::Expr {
|
||||
// TODO: Check for variables.
|
||||
|
||||
q!(
|
||||
Vars {
|
||||
sym_value: self.sym.to_code(cx),
|
||||
},
|
||||
{
|
||||
swc_ecma_quote::swc_ecma_ast::Ident::new(
|
||||
sym_value,
|
||||
swc_ecma_quote::swc_common::DUMMY_SP,
|
||||
)
|
||||
}
|
||||
)
|
||||
.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl_struct!(PrivateName, [span, id]);
|
48
crates/swc_ecma_quote_macros/src/ast/lit.rs
Normal file
48
crates/swc_ecma_quote_macros/src/ast/lit.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use pmutil::q;
|
||||
use proc_macro2::Span;
|
||||
use swc_atoms::JsWord;
|
||||
use swc_ecma_ast::*;
|
||||
use syn::{ExprLit, LitBool, LitFloat};
|
||||
|
||||
use super::ToCode;
|
||||
use crate::ctxt::Ctx;
|
||||
|
||||
fail_todo!(BigInt);
|
||||
fail_todo!(JSXText);
|
||||
|
||||
impl_struct!(Str, [span, value, has_escape, kind]);
|
||||
|
||||
impl ToCode for StrKind {
|
||||
fn to_code(&self, _: &Ctx) -> syn::Expr {
|
||||
q!(Vars {}, { Default::default() }).parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl_struct!(Bool, [span, value]);
|
||||
impl_struct!(Null, [span]);
|
||||
impl_struct!(Number, [span, value]);
|
||||
impl_struct!(Regex, [span, exp, flags]);
|
||||
|
||||
impl ToCode for JsWord {
|
||||
fn to_code(&self, _: &Ctx) -> syn::Expr {
|
||||
q!(Vars { val: &**self }, { val.into() }).parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCode for bool {
|
||||
fn to_code(&self, _: &Ctx) -> syn::Expr {
|
||||
syn::Expr::Lit(ExprLit {
|
||||
attrs: Default::default(),
|
||||
lit: syn::Lit::Bool(LitBool::new(*self, Span::call_site())),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCode for f64 {
|
||||
fn to_code(&self, _: &Ctx) -> syn::Expr {
|
||||
syn::Expr::Lit(ExprLit {
|
||||
attrs: Default::default(),
|
||||
lit: syn::Lit::Float(LitFloat::new(&self.to_string(), Span::call_site())),
|
||||
})
|
||||
}
|
||||
}
|
192
crates/swc_ecma_quote_macros/src/ast/mod.rs
Normal file
192
crates/swc_ecma_quote_macros/src/ast/mod.rs
Normal file
@ -0,0 +1,192 @@
|
||||
use pmutil::q;
|
||||
use swc_common::Span;
|
||||
use swc_ecma_ast::*;
|
||||
use syn::ExprBlock;
|
||||
|
||||
use crate::ctxt::Ctx;
|
||||
|
||||
macro_rules! fail_todo {
|
||||
($T:ty) => {
|
||||
impl crate::ast::ToCode for $T {
|
||||
fn to_code(&self, _: &crate::ctxt::Ctx) -> syn::Expr {
|
||||
todo!("ToCode for {}", stringify!($T))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_enum {
|
||||
($E:ident, [ $($v:ident),* ]) => {
|
||||
impl crate::ast::ToCode for $E {
|
||||
fn to_code(&self, cx: &crate::ctxt::Ctx) -> syn::Expr {
|
||||
match self {
|
||||
$(
|
||||
$E::$v(inner) => pmutil::q!(
|
||||
Vars {
|
||||
val: crate::ast::ToCode::to_code(inner, cx),
|
||||
},
|
||||
{ swc_ecma_quote::swc_ecma_ast::$E::$v(val) }
|
||||
)
|
||||
.parse(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_struct {
|
||||
(
|
||||
$name:ident,
|
||||
[ $($v:ident),* ]
|
||||
) => {
|
||||
impl crate::ast::ToCode for $name {
|
||||
fn to_code(&self, cx: &crate::ctxt::Ctx) -> syn::Expr {
|
||||
let mut builder = crate::builder::Builder::new(stringify!($name));
|
||||
|
||||
$(
|
||||
builder.add(
|
||||
stringify!($v),
|
||||
crate::ast::ToCode::to_code(&self.$v, cx),
|
||||
);
|
||||
)*
|
||||
|
||||
syn::Expr::Struct(builder.build())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mod class;
|
||||
mod decl;
|
||||
mod enums;
|
||||
mod expr;
|
||||
mod id;
|
||||
mod lit;
|
||||
mod module_decl;
|
||||
mod pat;
|
||||
mod prop;
|
||||
mod stmt;
|
||||
mod typescript;
|
||||
|
||||
pub(crate) trait ToCode: 'static {
|
||||
fn to_code(&self, cx: &Ctx) -> syn::Expr;
|
||||
}
|
||||
|
||||
impl<T> ToCode for Box<T>
|
||||
where
|
||||
T: ?Sized + ToCode,
|
||||
{
|
||||
fn to_code(&self, cx: &Ctx) -> syn::Expr {
|
||||
q!(
|
||||
Vars {
|
||||
inner: (**self).to_code(cx)
|
||||
},
|
||||
{ Box::new(inner) }
|
||||
)
|
||||
.parse()
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: Optimize
|
||||
impl<T> ToCode for Option<T>
|
||||
where
|
||||
T: ToCode,
|
||||
{
|
||||
fn to_code(&self, cx: &Ctx) -> syn::Expr {
|
||||
match self {
|
||||
Some(inner) => q!(
|
||||
Vars {
|
||||
inner: inner.to_code(cx)
|
||||
},
|
||||
{ Some(inner) }
|
||||
)
|
||||
.parse(),
|
||||
None => q!({ None }).parse(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_struct!(Invalid, [span]);
|
||||
|
||||
impl ToCode for Span {
|
||||
fn to_code(&self, _: &Ctx) -> syn::Expr {
|
||||
q!({ swc_ecma_quote::swc_common::DUMMY_SP }).parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl_enum!(ModuleItem, [ModuleDecl, Stmt]);
|
||||
|
||||
impl_enum!(Pat, [Ident, Array, Rest, Object, Assign, Invalid, Expr]);
|
||||
impl_enum!(Lit, [Str, Bool, Null, Num, BigInt, Regex, JSXText]);
|
||||
impl_enum!(
|
||||
ClassMember,
|
||||
[
|
||||
Constructor,
|
||||
Method,
|
||||
PrivateMethod,
|
||||
ClassProp,
|
||||
PrivateProp,
|
||||
TsIndexSignature,
|
||||
Empty,
|
||||
StaticBlock
|
||||
]
|
||||
);
|
||||
impl_enum!(ObjectPatProp, [KeyValue, Assign, Rest]);
|
||||
impl_enum!(PropName, [Ident, Str, Num, Computed, BigInt]);
|
||||
impl_enum!(ParamOrTsParamProp, [TsParamProp, Param]);
|
||||
impl_enum!(PropOrSpread, [Spread, Prop]);
|
||||
impl_enum!(BlockStmtOrExpr, [BlockStmt, Expr]);
|
||||
impl_enum!(MemberProp, [Ident, PrivateName, Computed]);
|
||||
impl_enum!(SuperProp, [Ident, Computed]);
|
||||
impl_enum!(JSXObject, [Ident, JSXMemberExpr]);
|
||||
impl_enum!(
|
||||
JSXElementChild,
|
||||
[
|
||||
JSXText,
|
||||
JSXElement,
|
||||
JSXExprContainer,
|
||||
JSXFragment,
|
||||
JSXSpreadChild
|
||||
]
|
||||
);
|
||||
impl_enum!(OptChainBase, [Member, Call]);
|
||||
impl_enum!(JSXElementName, [Ident, JSXMemberExpr, JSXNamespacedName]);
|
||||
impl_enum!(JSXAttrOrSpread, [JSXAttr, SpreadElement]);
|
||||
|
||||
impl<T> ToCode for Vec<T>
|
||||
where
|
||||
T: ToCode,
|
||||
{
|
||||
fn to_code(&self, cx: &Ctx) -> syn::Expr {
|
||||
let var_stmt = q!(Vars { len: self.len() }, {
|
||||
let mut items = Vec::with_capacity(len);
|
||||
})
|
||||
.parse::<syn::Stmt>();
|
||||
let mut stmts = vec![var_stmt];
|
||||
|
||||
for item in self {
|
||||
stmts.push(syn::Stmt::Semi(
|
||||
q!(
|
||||
Vars {
|
||||
item: item.to_code(cx)
|
||||
},
|
||||
{ items.push(item) }
|
||||
)
|
||||
.parse(),
|
||||
Default::default(),
|
||||
));
|
||||
}
|
||||
|
||||
stmts.push(syn::Stmt::Expr(q!(Vars {}, { items }).parse()));
|
||||
|
||||
syn::Expr::Block(ExprBlock {
|
||||
attrs: Default::default(),
|
||||
label: Default::default(),
|
||||
block: syn::Block {
|
||||
brace_token: Default::default(),
|
||||
stmts,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
39
crates/swc_ecma_quote_macros/src/ast/module_decl.rs
Normal file
39
crates/swc_ecma_quote_macros/src/ast/module_decl.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
impl_enum!(
|
||||
ModuleDecl,
|
||||
[
|
||||
Import,
|
||||
ExportDecl,
|
||||
ExportNamed,
|
||||
ExportDefaultDecl,
|
||||
ExportDefaultExpr,
|
||||
ExportAll,
|
||||
TsImportEquals,
|
||||
TsExportAssignment,
|
||||
TsNamespaceExport
|
||||
]
|
||||
);
|
||||
|
||||
impl_struct!(ImportDecl, [span, specifiers, src, type_only, asserts]);
|
||||
impl_struct!(ExportDecl, [span, decl]);
|
||||
impl_struct!(ExportDefaultDecl, [span, decl]);
|
||||
impl_struct!(ExportDefaultExpr, [span, expr]);
|
||||
impl_struct!(ExportAll, [span, src, asserts]);
|
||||
impl_struct!(NamedExport, [span, specifiers, src, type_only, asserts]);
|
||||
|
||||
impl_enum!(ImportSpecifier, [Named, Default, Namespace]);
|
||||
|
||||
impl_struct!(ImportNamedSpecifier, [span, local, imported, is_type_only]);
|
||||
impl_struct!(ImportDefaultSpecifier, [span, local]);
|
||||
impl_struct!(ImportStarAsSpecifier, [span, local]);
|
||||
|
||||
impl_enum!(ExportSpecifier, [Named, Default, Namespace]);
|
||||
|
||||
impl_enum!(DefaultDecl, [Class, Fn, TsInterfaceDecl]);
|
||||
|
||||
impl_enum!(ModuleExportName, [Ident, Str]);
|
||||
|
||||
impl_struct!(ExportNamedSpecifier, [span, orig, exported, is_type_only]);
|
||||
impl_struct!(ExportDefaultSpecifier, [exported]);
|
||||
impl_struct!(ExportNamespaceSpecifier, [span, name]);
|
8
crates/swc_ecma_quote_macros/src/ast/pat.rs
Normal file
8
crates/swc_ecma_quote_macros/src/ast/pat.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
impl_struct!(BindingIdent, [id, type_ann]);
|
||||
impl_struct!(ArrayPat, [span, elems, optional, type_ann]);
|
||||
impl_struct!(ObjectPat, [span, props, optional, type_ann]);
|
||||
impl_struct!(RestPat, [span, dot3_token, arg, type_ann]);
|
||||
impl_struct!(AssignPat, [span, left, right, type_ann]);
|
||||
impl_struct!(Param, [span, decorators, pat]);
|
37
crates/swc_ecma_quote_macros/src/ast/prop.rs
Normal file
37
crates/swc_ecma_quote_macros/src/ast/prop.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
impl_enum!(Prop, [Shorthand, KeyValue, Assign, Getter, Setter, Method]);
|
||||
|
||||
impl_struct!(
|
||||
PrivateProp,
|
||||
[
|
||||
span,
|
||||
key,
|
||||
value,
|
||||
type_ann,
|
||||
is_static,
|
||||
decorators,
|
||||
computed,
|
||||
accessibility,
|
||||
is_abstract,
|
||||
is_optional,
|
||||
is_override,
|
||||
readonly,
|
||||
definite
|
||||
]
|
||||
);
|
||||
|
||||
impl_struct!(KeyValueProp, [key, value]);
|
||||
|
||||
impl_struct!(AssignProp, [key, value]);
|
||||
|
||||
impl_struct!(GetterProp, [span, key, type_ann, body]);
|
||||
impl_struct!(SetterProp, [span, key, param, body]);
|
||||
|
||||
impl_struct!(MethodProp, [key, function]);
|
||||
|
||||
impl_struct!(KeyValuePatProp, [key, value]);
|
||||
|
||||
impl_struct!(AssignPatProp, [span, key, value]);
|
||||
|
||||
impl_struct!(ComputedPropName, [span, expr]);
|
35
crates/swc_ecma_quote_macros/src/ast/stmt.rs
Normal file
35
crates/swc_ecma_quote_macros/src/ast/stmt.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
impl_enum!(
|
||||
Stmt,
|
||||
[
|
||||
Block, Empty, Debugger, With, Return, Labeled, Break, Continue, If, Switch, Throw, Try,
|
||||
While, DoWhile, For, ForIn, ForOf, Decl, Expr
|
||||
]
|
||||
);
|
||||
|
||||
impl_struct!(EmptyStmt, [span]);
|
||||
impl_struct!(BlockStmt, [span, stmts]);
|
||||
impl_struct!(DebuggerStmt, [span]);
|
||||
impl_struct!(WithStmt, [span, obj, body]);
|
||||
impl_struct!(LabeledStmt, [span, label, body]);
|
||||
impl_struct!(BreakStmt, [span, label]);
|
||||
impl_struct!(ContinueStmt, [span, label]);
|
||||
impl_struct!(IfStmt, [span, test, cons, alt]);
|
||||
impl_struct!(SwitchStmt, [span, discriminant, cases]);
|
||||
impl_struct!(ThrowStmt, [span, arg]);
|
||||
impl_struct!(TryStmt, [span, block, handler, finalizer]);
|
||||
impl_struct!(WhileStmt, [span, test, body]);
|
||||
impl_struct!(DoWhileStmt, [span, test, body]);
|
||||
impl_struct!(ForStmt, [span, init, test, update, body]);
|
||||
impl_struct!(ForInStmt, [span, left, right, body]);
|
||||
impl_struct!(ForOfStmt, [span, await_token, left, right, body]);
|
||||
impl_struct!(ReturnStmt, [span, arg]);
|
||||
impl_struct!(ExprStmt, [span, expr]);
|
||||
|
||||
impl_enum!(VarDeclOrExpr, [VarDecl, Expr]);
|
||||
impl_enum!(VarDeclOrPat, [VarDecl, Pat]);
|
||||
|
||||
impl_struct!(SwitchCase, [span, test, cons]);
|
||||
|
||||
impl_struct!(CatchClause, [span, param, body]);
|
21
crates/swc_ecma_quote_macros/src/ast/typescript.rs
Normal file
21
crates/swc_ecma_quote_macros/src/ast/typescript.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
fail_todo!(TsInterfaceDecl);
|
||||
fail_todo!(TsTypeAliasDecl);
|
||||
fail_todo!(TsEnumDecl);
|
||||
fail_todo!(TsModuleDecl);
|
||||
fail_todo!(TsImportEqualsDecl);
|
||||
fail_todo!(TsExportAssignment);
|
||||
fail_todo!(TsNamespaceExportDecl);
|
||||
fail_todo!(TsTypeAssertion);
|
||||
fail_todo!(TsConstAssertion);
|
||||
fail_todo!(TsNonNullExpr);
|
||||
fail_todo!(TsAsExpr);
|
||||
fail_todo!(TsInstantiation);
|
||||
fail_todo!(TsType);
|
||||
fail_todo!(TsTypeAnn);
|
||||
fail_todo!(TsTypeParamInstantiation);
|
||||
fail_todo!(TsTypeParamDecl);
|
||||
fail_todo!(TsExprWithTypeArgs);
|
||||
fail_todo!(TsIndexSignature);
|
||||
fail_todo!(TsParamProp);
|
38
crates/swc_ecma_quote_macros/src/builder.rs
Normal file
38
crates/swc_ecma_quote_macros/src/builder.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use proc_macro2::Span;
|
||||
use syn::{punctuated::Punctuated, Expr, ExprStruct, FieldValue, Ident, Member, Token};
|
||||
|
||||
pub(crate) struct Builder {
|
||||
type_name: syn::Ident,
|
||||
fields: Punctuated<FieldValue, Token![,]>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn new(ident: &str) -> Self {
|
||||
Self {
|
||||
type_name: Ident::new(ident, Span::call_site()),
|
||||
fields: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, name: &str, value: Expr) {
|
||||
self.fields.push(FieldValue {
|
||||
attrs: Default::default(),
|
||||
member: Member::Named(Ident::new(name, Span::call_site())),
|
||||
colon_token: Some(Default::default()),
|
||||
expr: value,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn build(self) -> ExprStruct {
|
||||
let type_name = self.type_name;
|
||||
|
||||
ExprStruct {
|
||||
attrs: Default::default(),
|
||||
brace_token: Default::default(),
|
||||
path: syn::parse_quote!(swc_ecma_quote::swc_ecma_ast::#type_name),
|
||||
fields: self.fields,
|
||||
dot2_token: Default::default(),
|
||||
rest: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
20
crates/swc_ecma_quote_macros/src/ctxt.rs
Normal file
20
crates/swc_ecma_quote_macros/src/ctxt.rs
Normal file
@ -0,0 +1,20 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use swc_common::collections::AHashMap;
|
||||
use swc_ecma_ast::Ident;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Ctx {
|
||||
pub(crate) vars: Vars,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VarData {
|
||||
/// How many times this variable should be cloned. 0 for variables used only
|
||||
/// once.
|
||||
clone: usize,
|
||||
|
||||
ident: Ident,
|
||||
}
|
||||
|
||||
pub type Vars = AHashMap<String, VarData>;
|
54
crates/swc_ecma_quote_macros/src/input.rs
Normal file
54
crates/swc_ecma_quote_macros/src/input.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
Token,
|
||||
};
|
||||
|
||||
pub(super) struct QuoteInput {
|
||||
pub src: syn::LitStr,
|
||||
#[allow(unused)]
|
||||
pub as_token: Token![as],
|
||||
pub output_type: syn::Type,
|
||||
|
||||
pub vars: Option<(Token![,], Punctuated<QuoteVar, Token![,]>)>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(super) struct QuoteVar {
|
||||
pub name: syn::Ident,
|
||||
#[allow(unused)]
|
||||
pub eq_token: Token![=],
|
||||
pub value: syn::Expr,
|
||||
}
|
||||
|
||||
impl Parse for QuoteInput {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let src = input.parse()?;
|
||||
let as_token = input.parse()?;
|
||||
let output_type = input.parse()?;
|
||||
let vars = if input.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let comma_token = input.parse()?;
|
||||
let vars = Punctuated::parse_terminated(input)?;
|
||||
Some((comma_token, vars))
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
src,
|
||||
as_token,
|
||||
output_type,
|
||||
vars,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for QuoteVar {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Ok(Self {
|
||||
name: input.parse()?,
|
||||
eq_token: input.parse()?,
|
||||
value: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
40
crates/swc_ecma_quote_macros/src/lib.rs
Normal file
40
crates/swc_ecma_quote_macros/src/lib.rs
Normal file
@ -0,0 +1,40 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use quote::ToTokens;
|
||||
|
||||
use crate::{ast::ToCode, ctxt::Ctx, input::QuoteInput, ret_type::parse_input_type};
|
||||
|
||||
mod ast;
|
||||
mod builder;
|
||||
mod ctxt;
|
||||
mod input;
|
||||
mod ret_type;
|
||||
|
||||
/// Don't invoke this macro directly, use the `quote!` macro from
|
||||
/// `swc_ecma_quote` instead.
|
||||
#[proc_macro]
|
||||
pub fn internal_quote(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let QuoteInput {
|
||||
src,
|
||||
as_token: _,
|
||||
output_type,
|
||||
vars,
|
||||
} = syn::parse::<QuoteInput>(input).expect("failed to parse input to quote!()");
|
||||
|
||||
let ret_type =
|
||||
parse_input_type(&src.value(), &output_type).expect("failed to parse input type");
|
||||
|
||||
let vars = vars.map(|v| v.1);
|
||||
let map = HashMap::default();
|
||||
|
||||
if let Some(vars) = vars {
|
||||
if !vars.is_empty() {
|
||||
todo!("quote! macro does not support variables yet")
|
||||
}
|
||||
}
|
||||
|
||||
let cx = Ctx { vars: map };
|
||||
ret_type.to_code(&cx).to_token_stream().into()
|
||||
}
|
94
crates/swc_ecma_quote_macros/src/ret_type.rs
Normal file
94
crates/swc_ecma_quote_macros/src/ret_type.rs
Normal file
@ -0,0 +1,94 @@
|
||||
use std::any::type_name;
|
||||
|
||||
use anyhow::{anyhow, bail, Context, Error};
|
||||
use swc_common::{sync::Lrc, FileName, SourceMap};
|
||||
use swc_ecma_ast::EsVersion;
|
||||
use swc_ecma_parser::{lexer::Lexer, PResult, Parser, StringInput};
|
||||
use syn::{GenericArgument, PathArguments, Type};
|
||||
|
||||
use crate::{ast::ToCode, ctxt::Ctx};
|
||||
|
||||
/// Storage for `dyn ToCode`.The first `Box`, which is required to store `dyn
|
||||
/// ToCode`, is ignored.
|
||||
pub struct BoxWrapper(Box<dyn ToCode>);
|
||||
|
||||
impl ToCode for BoxWrapper {
|
||||
fn to_code(&self, cx: &Ctx) -> syn::Expr {
|
||||
(*self.0).to_code(cx)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_input_type(input_str: &str, ty: &Type) -> Result<BoxWrapper, Error> {
|
||||
if let Some(ty) = extract_generic("Box", ty) {
|
||||
let node = parse_input_type(input_str, ty).context("failed to parse `T` in Box<T>")?;
|
||||
return Ok(BoxWrapper(Box::new(Box::new(node))));
|
||||
}
|
||||
|
||||
if let Some(ty) = extract_generic("Option", ty) {
|
||||
if input_str.is_empty() {
|
||||
return Ok(BoxWrapper(Box::new(None::<swc_ecma_ast::Expr>)));
|
||||
}
|
||||
|
||||
let node = parse_input_type(input_str, ty).context("failed to parse `T` in Option<T>")?;
|
||||
return Ok(BoxWrapper(Box::new(Some(node))));
|
||||
}
|
||||
|
||||
if let Type::Path(p) = ty {
|
||||
if let Some(ident) = p.path.get_ident() {
|
||||
match &*ident.to_string() {
|
||||
"Expr" => return parse(input_str, &mut |p| p.parse_expr().map(|v| *v)),
|
||||
"Pat" => return parse(input_str, &mut |p| p.parse_pat()),
|
||||
"Stmt" => return parse(input_str, &mut |p| p.parse_stmt(true)),
|
||||
"ModuleItem" => return parse(input_str, &mut |p| p.parse_module_item()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bail!("Unknown quote type: {:?}", ty);
|
||||
}
|
||||
|
||||
fn parse<T>(
|
||||
input_str: &str,
|
||||
op: &mut dyn FnMut(&mut Parser<Lexer<StringInput>>) -> PResult<T>,
|
||||
) -> Result<BoxWrapper, Error>
|
||||
where
|
||||
T: ToCode,
|
||||
{
|
||||
let cm = Lrc::new(SourceMap::default());
|
||||
let fm = cm.new_source_file(FileName::Anon, input_str.to_string());
|
||||
|
||||
let lexer = Lexer::new(
|
||||
Default::default(),
|
||||
EsVersion::Es2020,
|
||||
StringInput::from(&*fm),
|
||||
None,
|
||||
);
|
||||
let mut parser = Parser::new_from(lexer);
|
||||
op(&mut parser)
|
||||
.map_err(|err| anyhow!("{:?}", err))
|
||||
.with_context(|| format!("failed to parse input as `{}`", type_name::<T>()))
|
||||
.map(|val| BoxWrapper(Box::new(val)))
|
||||
}
|
||||
|
||||
fn extract_generic<'a>(name: &str, ty: &'a Type) -> Option<&'a Type> {
|
||||
if let Type::Path(p) = ty {
|
||||
let last = p.path.segments.last().unwrap();
|
||||
|
||||
if !last.arguments.is_empty() && last.ident == name {
|
||||
match &last.arguments {
|
||||
PathArguments::AngleBracketed(tps) => {
|
||||
let arg = tps.args.first().unwrap();
|
||||
|
||||
match arg {
|
||||
GenericArgument::Type(arg) => return Some(arg),
|
||||
_ => unimplemented!("generic parameter other than type"),
|
||||
}
|
||||
}
|
||||
_ => unimplemented!("Box() -> T or Box without a type parameter"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
Loading…
Reference in New Issue
Block a user