diff --git a/Cargo.toml b/Cargo.toml index 05687f80b07..dd65f0e68be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,4 +29,8 @@ features = [ "suggestions", "color" ] [profile.bench] -debug = true \ No newline at end of file +debug = true + + +[patch.crates-io] +darling = { git = "https://github.com/kdy1/darling", branch = "proc-macro2-nightly" } \ No newline at end of file diff --git a/ecmascript/ast/src/decl.rs b/ecmascript/ast/src/decl.rs index b805aa5946b..8ab93872b5c 100644 --- a/ecmascript/ast/src/decl.rs +++ b/ecmascript/ast/src/decl.rs @@ -12,13 +12,14 @@ pub enum Decl { #[ast_node] pub struct FnDecl { pub ident: Ident, + #[span] pub function: Function, } #[ast_node] pub struct ClassDecl { pub ident: Ident, - + #[span] pub class: Class, } diff --git a/ecmascript/ast/src/expr.rs b/ecmascript/ast/src/expr.rs index 85d920e2737..d71f2177cdb 100644 --- a/ecmascript/ast/src/expr.rs +++ b/ecmascript/ast/src/expr.rs @@ -1,6 +1,6 @@ use super::{AssignOp, BinaryOp, BlockStmt, Class, Function, Ident, Lit, Pat, Prop, UnaryOp, UpdateOp}; -use swc_common::Span; +use swc_common::{Span, Spanned}; use swc_macros::ast_node; #[ast_node] @@ -114,7 +114,7 @@ pub struct BinExpr { #[ast_node] pub struct FnExpr { pub ident: Option, - + #[span] pub function: Function, } @@ -122,7 +122,7 @@ pub struct FnExpr { #[ast_node] pub struct ClassExpr { pub ident: Option, - + #[span] pub class: Class, } @@ -136,34 +136,42 @@ pub struct AssignExpr { #[ast_node] pub struct MemberExpr { + pub span: Span, pub obj: ExprOrSuper, pub prop: Box, pub computed: bool, } #[ast_node] pub struct CondExpr { + #[span(lo)] pub test: Box, pub cons: Box, + #[span(hi)] pub alt: Box, } #[ast_node] pub struct CallExpr { + pub span: Span, pub callee: ExprOrSuper, pub args: Vec, } #[ast_node] pub struct NewExpr { + pub span: Span, pub callee: Box, - // #[code = "$( $( $args ),* )?"] pub args: Option<(Vec)>, } #[ast_node] pub struct SeqExpr { + /// TODO: Calculate + pub span: Span, pub exprs: Vec<(Box)>, } + #[ast_node] pub struct ArrowExpr { + pub span: Span, pub params: Vec, pub body: BlockStmtOrExpr, @@ -173,21 +181,26 @@ pub struct ArrowExpr { #[ast_node] pub struct YieldExpr { + pub span: Span, pub arg: Option<(Box)>, pub delegate: bool, } #[ast_node] pub struct MetaPropExpr { + #[span(lo)] pub meta: Ident, + #[span(hi)] pub prop: Ident, } #[ast_node] pub struct AwaitExpr { + pub span: Span, pub arg: Box, } #[ast_node] pub struct TplLit { + pub span: Span, pub tag: Option<(Box)>, pub exprs: Vec<(Box)>, @@ -197,6 +210,7 @@ pub struct TplLit { #[ast_node] pub struct TplElement { + pub span: Span, pub tail: bool, pub cooked: bool, pub raw: String, @@ -214,11 +228,20 @@ pub enum ExprOrSuper { Expr(Box), } -#[ast_node] +#[derive(Fold, Clone, Debug, PartialEq)] pub struct ExprOrSpread { pub spread: Option, pub expr: Box, } +impl Spanned for ExprOrSpread { + fn span(&self) -> Span { + let expr = self.expr.span(); + match self.spread { + Some(spread) => expr.with_lo(spread.lo()), + None => expr, + } + } +} #[ast_node] pub enum BlockStmtOrExpr { diff --git a/ecmascript/ast/src/lib.rs b/ecmascript/ast/src/lib.rs index d04d24d877a..2bf67b9d088 100644 --- a/ecmascript/ast/src/lib.rs +++ b/ecmascript/ast/src/lib.rs @@ -12,15 +12,18 @@ pub use self::class::{Class, ClassMethod, ClassMethodKind}; pub use self::decl::{ClassDecl, Decl, FnDecl, VarDecl, VarDeclKind, VarDeclarator}; pub use self::expr::{ArrayLit, ArrowExpr, AssignExpr, AwaitExpr, BinExpr, BlockStmtOrExpr, CallExpr, ClassExpr, CondExpr, Expr, ExprOrSpread, ExprOrSuper, FnExpr, - MemberExpr, MetaPropExpr, NewExpr, ObjectLit, PatOrExpr, SeqExpr, TplElement, - TplLit, UnaryExpr, UpdateExpr, YieldExpr}; + MemberExpr, MetaPropExpr, NewExpr, ObjectLit, ParenExpr, PatOrExpr, SeqExpr, + ThisExpr, TplElement, TplLit, UnaryExpr, UpdateExpr, YieldExpr}; pub use self::function::Function; pub use self::lit::{Bool, Lit, Null, Number, Regex, RegexFlags, Str}; pub use self::module::{Module, ModuleItem}; -pub use self::module_decl::{ExportDefaultDecl, ExportSpecifier, ImportSpecifier, ModuleDecl}; +pub use self::module_decl::{ExportAll, ExportDefaultDecl, ExportSpecifier, ImportDecl, + ImportDefault, ImportSpecific, ImportSpecifier, ImportStarAs, + ModuleDecl, NamedExport}; pub use self::operators::{AssignOp, BinaryOp, UnaryOp, UpdateOp}; -pub use self::pat::{ObjectPatProp, Pat}; -pub use self::prop::{Prop, PropName}; +pub use self::pat::{ArrayPat, AssignPat, AssignPatProp, KeyValuePatProp, ObjectPat, ObjectPatProp, + Pat}; +pub use self::prop::{AssignProp, GetterProp, KeyValueProp, MethodProp, Prop, PropName, SetterProp}; pub use self::stmt::{BlockStmt, BreakStmt, CatchClause, ContinueStmt, DebuggerStmt, DoWhileStmt, EmptyStmt, ForInStmt, ForOfStmt, ForStmt, IfStmt, LabeledStmt, ReturnStmt, Stmt, SwitchCase, SwitchStmt, ThrowStmt, TryStmt, VarDeclOrExpr, diff --git a/ecmascript/ast/src/module_decl.rs b/ecmascript/ast/src/module_decl.rs index 2b0ce17d2ab..7e5c9fb7c53 100644 --- a/ecmascript/ast/src/module_decl.rs +++ b/ecmascript/ast/src/module_decl.rs @@ -78,6 +78,7 @@ pub struct ImportSpecific { #[ast_node] pub struct ExportSpecifier { + pub span: Span, /// `foo` in `export { foo as bar }` pub orig: Ident, /// `Some(bar)` in `export { foo as bar }` diff --git a/ecmascript/ast/src/pat.rs b/ecmascript/ast/src/pat.rs index 8216ec618a5..abd4892103d 100644 --- a/ecmascript/ast/src/pat.rs +++ b/ecmascript/ast/src/pat.rs @@ -10,7 +10,7 @@ pub enum Pat { Rest(Box), - Object(Vec), + Object(ObjectPat), Assign(AssignPat), @@ -24,6 +24,12 @@ pub struct ArrayPat { pub elems: Vec<(Option)>, } +#[ast_node] +pub struct ObjectPat { + pub span: Span, + pub props: Vec, +} + #[ast_node] pub struct AssignPat { pub span: Span, @@ -40,12 +46,15 @@ pub enum ObjectPatProp { /// `{key: value}` #[ast_node] pub struct KeyValuePatProp { + #[span(lo)] pub key: PropName, + #[span(hi)] pub value: Box, } /// `{key}` or `{key = value}` #[ast_node] pub struct AssignPatProp { + pub span: Span, pub key: Ident, pub value: Option<(Box)>, diff --git a/ecmascript/ast/src/prop.rs b/ecmascript/ast/src/prop.rs index a2a1d8e2c01..fd316c69814 100644 --- a/ecmascript/ast/src/prop.rs +++ b/ecmascript/ast/src/prop.rs @@ -18,18 +18,18 @@ pub enum Prop { #[ast_node] pub struct KeyValueProp { - // #[span(lo)] + #[span(lo)] pub key: PropName, - // #[span(hi)] + #[span(hi)] pub value: Box, } #[ast_node] pub struct AssignProp { - // #[span(lo)] + #[span(lo)] key: Ident, - // #[span(hi)] + #[span(hi)] value: Box, } #[ast_node] @@ -47,9 +47,9 @@ pub struct SetterProp { } #[ast_node] pub struct MethodProp { - // #[span(lo)] + #[span(lo)] key: PropName, - // #[span(hi)] + #[span(hi)] function: Function, } diff --git a/macros/ast_node/Cargo.toml b/macros/ast_node/Cargo.toml index 88690c468d8..0a54cd11924 100644 --- a/macros/ast_node/Cargo.toml +++ b/macros/ast_node/Cargo.toml @@ -11,6 +11,7 @@ swc_macros_common = { path = "../common" } pmutil = "0.1" proc-macro2 = { version = "0.2", features = ["nightly"] } quote = "0.4" +darling = "0.3" [dependencies.syn] version = "0.12" diff --git a/macros/ast_node/src/lib.rs b/macros/ast_node/src/lib.rs index c3e090d8ab5..502a2d8f494 100644 --- a/macros/ast_node/src/lib.rs +++ b/macros/ast_node/src/lib.rs @@ -1,5 +1,7 @@ #![feature(box_syntax, proc_macro)] +#[macro_use] +extern crate darling; #[macro_use] extern crate pmutil; extern crate proc_macro; diff --git a/macros/ast_node/src/spanned.rs b/macros/ast_node/src/spanned.rs index 336958ba68a..6572f21d277 100644 --- a/macros/ast_node/src/spanned.rs +++ b/macros/ast_node/src/spanned.rs @@ -1,5 +1,22 @@ +use darling::FromField; use swc_macros_common::prelude::*; +#[derive(Debug, FromField)] +#[darling(attributes(span))] +struct MyField { + /// Name of the field. + pub ident: Option, + /// Type of the field. + pub ty: Type, + + /// `#[span(lo)]` + #[darling(default)] + pub lo: bool, + /// `#[span(hi)]` + #[darling(default)] + pub hi: bool, +} + pub fn derive(input: DeriveInput) -> ItemImpl { let arms = Binder::new_from(&input) .variants() @@ -53,6 +70,15 @@ pub fn derive(input: DeriveInput) -> ItemImpl { } fn make_body_for_variant(v: &VariantBinder, bindings: Vec) -> Box { + /// `swc_common::Spanned::span(#field)` + fn simple_field(field: &ToTokens) -> Box { + box Quote::new(Span::def_site()) + .quote_with(smart_quote!(Vars { field }, { + swc_common::Spanned::span(field) + })) + .parse() + } + if bindings.len() == 0 { panic!("#[derive(Spanned)] requires a field to get span from") } @@ -61,35 +87,75 @@ fn make_body_for_variant(v: &VariantBinder, bindings: Vec) -> Box { // Call self.0.span() - return box Quote::new(Span::def_site()) - .quote_with(smart_quote!( - Vars { - field: &bindings[0], - }, - { swc_common::Spanned::span(field) } - )) - .parse(); + return simple_field(&bindings[0]); } _ => {} } } - // TODO: Handle #[span] attribute. - - let span_field = bindings + // Handle #[span] attribute. + if let Some(f) = bindings .iter() - .find(|b| { - let s = b.field().ident.as_ref().map(|ident| ident.as_ref()); - Some("span") == s - }) - .unwrap_or_else(|| { - panic!( - "#[derive(Spanned)]: cannot determine span field to use for {}", - v.qual_path().dump() - ) - }); + .find(|b| has_empty_span_attr(&b.field().attrs)) + { + //TODO: Verify that there's no more #[span] + return simple_field(f); + } - box Quote::new(Span::def_site()) - .quote_with(smart_quote!(Vars { span_field }, { span_field })) - .parse() + // 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| Some("span") == b.field().ident.as_ref().map(|ident| ident.as_ref())) + .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()).unwrap())) + .collect(); + + // TODO: Only one field should be `#[span(lo)]`. + let lo = fields.iter().find(|&&(_, ref f)| f.lo); + let hi = fields.iter().find(|&&(_, ref f)| f.hi); + + match (lo, hi) { + (Some(&(ref lo_field, _)), Some(&(ref hi_field, _))) => { + // Create a new span from lo_field.lo(), hi_field.hi() + box Quote::new(Span::def_site()) + .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; + } + + attr.tts.is_empty() + }) } diff --git a/macros/ast_node/tests/attr_interop.rs b/macros/ast_node/tests/attr_interop.rs new file mode 100644 index 00000000000..a1343b96d71 --- /dev/null +++ b/macros/ast_node/tests/attr_interop.rs @@ -0,0 +1,24 @@ +//! Test that `#[span]` and `#[fold]` can be used at same time. +#![feature(proc_macro)] + +extern crate swc_common; +extern crate swc_macros; +use swc_common::{Fold, Span, Spanned}; +use swc_macros::ast_node; + +#[ast_node] +// See https://github.com/rust-lang/rust/issues/44925 +pub struct Class { + #[span] + pub has_span: HasSpan, + #[fold(ignore)] + pub s: String, +} + +#[ast_node] +pub struct Tuple(#[span] HasSpan, #[fold(ignore)] usize, usize); + +#[derive(Debug, Clone, PartialEq, Fold, Spanned)] +pub struct HasSpan { + pub span: Span, +} diff --git a/macros/ast_node/tests/attribute.rs b/macros/ast_node/tests/attribute.rs deleted file mode 100644 index 1eae69d863a..00000000000 --- a/macros/ast_node/tests/attribute.rs +++ /dev/null @@ -1,15 +0,0 @@ -#![feature(specialization, proc_macro)] - -extern crate swc_common; -extern crate swc_macros; -use swc_macros::ast_node; - -#[ast_node] -// See https://github.com/rust-lang/rust/issues/44925 -pub struct Class { - #[fold(ignore)] - pub s: String, -} - -#[ast_node] -pub struct Tuple(usize, usize); diff --git a/macros/ast_node/tests/empty.rs b/macros/ast_node/tests/empty.rs index f3ba7d060a3..937e38357fc 100644 --- a/macros/ast_node/tests/empty.rs +++ b/macros/ast_node/tests/empty.rs @@ -1,12 +1,12 @@ -#![feature(specialization, proc_macro)] +#![feature(proc_macro)] +extern crate ast_node; extern crate swc_common; -extern crate swc_macros; -use swc_macros::ast_node; +use ast_node::*; -#[ast_node] +#[derive(Debug, Fold)] pub struct Struct {} -#[ast_node] +#[derive(Debug, FromVariant, Fold)] pub enum Enum { } diff --git a/macros/ast_node/tests/generics.rs b/macros/ast_node/tests/fold_bound.rs similarity index 74% rename from macros/ast_node/tests/generics.rs rename to macros/ast_node/tests/fold_bound.rs index b40d543b998..16baa967d2b 100644 --- a/macros/ast_node/tests/generics.rs +++ b/macros/ast_node/tests/fold_bound.rs @@ -1,30 +1,29 @@ -//! Ensures that #[derive(AstNode)] works with generic types. +//! Ensures that #[derive(Fold)] works with generic types. #![feature(specialization, proc_macro)] extern crate swc_common; extern crate swc_macros; use std::fmt::Debug; -use swc_common::AstNode; -use swc_macros::ast_node; +use swc_common::{AstNode, Fold}; pub trait Ast: Copy + Eq + Debug { type CustomExpr: AstNode; } -#[ast_node] +#[derive(Fold)] pub struct Stmt { #[fold(bound)] pub expr: Expr, } -#[ast_node] +#[derive(Fold)] pub struct Expr { #[fold(bound)] pub node: ExprKind, } -#[ast_node] +#[derive(Fold)] pub enum ExprKind { Custom(#[fold(bound)] A::CustomExpr), /// Recursive diff --git a/macros/ast_node/tests/fold.rs b/macros/ast_node/tests/fold_simple.rs similarity index 81% rename from macros/ast_node/tests/fold.rs rename to macros/ast_node/tests/fold_simple.rs index cc94cb11d43..f041857fa94 100644 --- a/macros/ast_node/tests/fold.rs +++ b/macros/ast_node/tests/fold_simple.rs @@ -2,15 +2,14 @@ extern crate swc_common; extern crate swc_macros; -use swc_common::{FoldWith, Folder}; -use swc_macros::ast_node; +use swc_common::{Fold, FoldWith}; -pub trait AssertFolder: Folder {} +pub trait AssertFolder: Fold {} // check for trait bound pub struct LitFolder; -impl Folder for LitFolder { +impl Fold for LitFolder { fn fold(&mut self, _: Lit) -> Lit { Lit::A } @@ -18,7 +17,7 @@ impl Folder for LitFolder { impl AssertFolder for LitFolder {} impl AssertFolder for LitFolder {} -#[ast_node] +#[derive(Debug, Fold, PartialEq, Eq)] pub struct Expr { pub node: ExprKind, /// This field should be skipped. @@ -40,7 +39,7 @@ impl FoldWith for PanicOnFold { } } -#[ast_node] +#[derive(Debug, Fold, PartialEq, Eq)] pub enum ExprKind { RecursiveBoud(Box), Rec2(Vec>>), @@ -48,7 +47,7 @@ pub enum ExprKind { Lit(Lit), } -#[ast_node] +#[derive(Debug, Fold, PartialEq, Eq)] pub enum Lit { A, B, diff --git a/macros/ast_node/tests/spanned_attr.rs b/macros/ast_node/tests/spanned_attr.rs new file mode 100644 index 00000000000..a7350b65cd3 --- /dev/null +++ b/macros/ast_node/tests/spanned_attr.rs @@ -0,0 +1,22 @@ +#![feature(proc_macro)] + +extern crate swc_common; +use swc_common::{BytePos, Span, Spanned}; + +#[test] +fn lo_hi() { + #[derive(Spanned)] + struct LoHi { + #[span(lo)] + pub lo: BytePos, + #[span(hi)] + pub hi: BytePos, + } + + let lo = BytePos(0); + let hi = BytePos(5); + assert_eq!( + LoHi { lo, hi }.span(), + Span::new(lo, hi, Default::default()) + ); +}