implement #[span]

This commit is contained in:
강동윤 2018-03-04 15:17:52 +09:00
parent 3321ca590d
commit 077e0c551f
16 changed files with 215 additions and 76 deletions

View File

@ -29,4 +29,8 @@ features = [ "suggestions", "color" ]
[profile.bench]
debug = true
debug = true
[patch.crates-io]
darling = { git = "https://github.com/kdy1/darling", branch = "proc-macro2-nightly" }

View File

@ -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,
}

View File

@ -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<Ident>,
#[span]
pub function: Function,
}
@ -122,7 +122,7 @@ pub struct FnExpr {
#[ast_node]
pub struct ClassExpr {
pub ident: Option<Ident>,
#[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<Expr>,
pub computed: bool,
}
#[ast_node]
pub struct CondExpr {
#[span(lo)]
pub test: Box<Expr>,
pub cons: Box<Expr>,
#[span(hi)]
pub alt: Box<Expr>,
}
#[ast_node]
pub struct CallExpr {
pub span: Span,
pub callee: ExprOrSuper,
pub args: Vec<ExprOrSpread>,
}
#[ast_node]
pub struct NewExpr {
pub span: Span,
pub callee: Box<Expr>,
// #[code = "$( $( $args ),* )?"]
pub args: Option<(Vec<ExprOrSpread>)>,
}
#[ast_node]
pub struct SeqExpr {
/// TODO: Calculate
pub span: Span,
pub exprs: Vec<(Box<Expr>)>,
}
#[ast_node]
pub struct ArrowExpr {
pub span: Span,
pub params: Vec<Pat>,
pub body: BlockStmtOrExpr,
@ -173,21 +181,26 @@ pub struct ArrowExpr {
#[ast_node]
pub struct YieldExpr {
pub span: Span,
pub arg: Option<(Box<Expr>)>,
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<Expr>,
}
#[ast_node]
pub struct TplLit {
pub span: Span,
pub tag: Option<(Box<Expr>)>,
pub exprs: Vec<(Box<Expr>)>,
@ -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<Expr>),
}
#[ast_node]
#[derive(Fold, Clone, Debug, PartialEq)]
pub struct ExprOrSpread {
pub spread: Option<Span>,
pub expr: Box<Expr>,
}
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 {

View File

@ -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,

View File

@ -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 }`

View File

@ -10,7 +10,7 @@ pub enum Pat {
Rest(Box<Pat>),
Object(Vec<ObjectPatProp>),
Object(ObjectPat),
Assign(AssignPat),
@ -24,6 +24,12 @@ pub struct ArrayPat {
pub elems: Vec<(Option<Pat>)>,
}
#[ast_node]
pub struct ObjectPat {
pub span: Span,
pub props: Vec<ObjectPatProp>,
}
#[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<Pat>,
}
/// `{key}` or `{key = value}`
#[ast_node]
pub struct AssignPatProp {
pub span: Span,
pub key: Ident,
pub value: Option<(Box<Expr>)>,

View File

@ -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<Expr>,
}
#[ast_node]
pub struct AssignProp {
// #[span(lo)]
#[span(lo)]
key: Ident,
// #[span(hi)]
#[span(hi)]
value: Box<Expr>,
}
#[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,
}

View File

@ -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"

View File

@ -1,5 +1,7 @@
#![feature(box_syntax, proc_macro)]
#[macro_use]
extern crate darling;
#[macro_use]
extern crate pmutil;
extern crate proc_macro;

View File

@ -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<Ident>,
/// 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<BindedField>) -> Box<Expr> {
/// `swc_common::Spanned::span(#field)`
fn simple_field(field: &ToTokens) -> Box<Expr> {
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<BindedField>) -> Box<E
match *v.data() {
Fields::Unnamed(..) => {
// 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()
})
}

View File

@ -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,
}

View File

@ -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);

View File

@ -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 {
}

View File

@ -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<A: Ast> {
#[fold(bound)]
pub expr: Expr<A>,
}
#[ast_node]
#[derive(Fold)]
pub struct Expr<A: Ast> {
#[fold(bound)]
pub node: ExprKind<A>,
}
#[ast_node]
#[derive(Fold)]
pub enum ExprKind<A: Ast> {
Custom(#[fold(bound)] A::CustomExpr),
/// Recursive

View File

@ -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<T>: Folder<T> {}
pub trait AssertFolder<T>: Fold<T> {}
// check for trait bound
pub struct LitFolder;
impl Folder<Lit> for LitFolder {
impl Fold<Lit> for LitFolder {
fn fold(&mut self, _: Lit) -> Lit {
Lit::A
}
@ -18,7 +17,7 @@ impl Folder<Lit> for LitFolder {
impl AssertFolder<Expr> for LitFolder {}
impl AssertFolder<ExprKind> 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<F> FoldWith<F> for PanicOnFold {
}
}
#[ast_node]
#[derive(Debug, Fold, PartialEq, Eq)]
pub enum ExprKind {
RecursiveBoud(Box<Expr>),
Rec2(Vec<Option<Box<Expr>>>),
@ -48,7 +47,7 @@ pub enum ExprKind {
Lit(Lit),
}
#[ast_node]
#[derive(Debug, Fold, PartialEq, Eq)]
pub enum Lit {
A,
B,

View File

@ -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())
);
}