From 2a2c9d38c78ece0a6c5de80e382a136173e64b14 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 26 Aug 2016 19:23:42 +0300 Subject: Improve shallow `Clone` deriving --- src/libsyntax_ext/deriving/clone.rs | 179 ++++++++++++++++++------------ src/libsyntax_ext/deriving/generic/mod.rs | 27 +++-- 2 files changed, 129 insertions(+), 77 deletions(-) (limited to 'src/libsyntax_ext') diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs index c7afaaf4796..aa7c2c301dd 100644 --- a/src/libsyntax_ext/deriving/clone.rs +++ b/src/libsyntax_ext/deriving/clone.rs @@ -11,20 +11,14 @@ use deriving::generic::*; use deriving::generic::ty::*; -use syntax::ast::{Expr, Generics, ItemKind, MetaItem, VariantData}; +use syntax::ast::{self, Expr, Generics, ItemKind, MetaItem, VariantData}; use syntax::attr; use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; -use syntax::parse::token::InternedString; +use syntax::parse::token::{keywords, InternedString}; use syntax::ptr::P; use syntax_pos::Span; -#[derive(PartialEq)] -enum Mode { - Deep, - Shallow, -} - pub fn expand_deriving_clone(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem, @@ -40,29 +34,38 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, // if we used the short form with generics, we'd have to bound the generics with // Clone + Copy, and then there'd be no Clone impl at all if the user fills in something // that is Clone but not Copy. and until specialization we can't write both impls. + // - the item is a union with Copy fields + // Unions with generic parameters still can derive Clone because they require Copy + // for deriving, Clone alone is not enough. + // Whever Clone is implemented for fields is irrelevant so we don't assert it. let bounds; - let unify_fieldless_variants; let substructure; + let is_shallow; match *item { Annotatable::Item(ref annitem) => { match annitem.node { ItemKind::Struct(_, Generics { ref ty_params, .. }) | ItemKind::Enum(_, Generics { ref ty_params, .. }) - if ty_params.is_empty() && - attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") => { - + if attr::contains_name(&annitem.attrs, "rustc_copy_clone_marker") && + ty_params.is_empty() => { + bounds = vec![]; + is_shallow = true; + substructure = combine_substructure(Box::new(|c, s, sub| { + cs_clone_shallow("Clone", c, s, sub, false) + })); + } + ItemKind::Union(..) => { bounds = vec![Literal(path_std!(cx, core::marker::Copy))]; - unify_fieldless_variants = true; + is_shallow = true; substructure = combine_substructure(Box::new(|c, s, sub| { - cs_clone("Clone", c, s, sub, Mode::Shallow) + cs_clone_shallow("Clone", c, s, sub, true) })); } - _ => { bounds = vec![]; - unify_fieldless_variants = false; + is_shallow = false; substructure = combine_substructure(Box::new(|c, s, sub| { - cs_clone("Clone", c, s, sub, Mode::Deep) + cs_clone("Clone", c, s, sub) })); } } @@ -80,7 +83,7 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, additional_bounds: bounds, generics: LifetimeBounds::empty(), is_unsafe: false, - supports_unions: false, + supports_unions: true, methods: vec![MethodDef { name: "clone", generics: LifetimeBounds::empty(), @@ -89,37 +92,85 @@ pub fn expand_deriving_clone(cx: &mut ExtCtxt, ret_ty: Self_, attributes: attrs, is_unsafe: false, - unify_fieldless_variants: unify_fieldless_variants, + unify_fieldless_variants: false, combine_substructure: substructure, }], associated_types: Vec::new(), }; - trait_def.expand(cx, mitem, item, push) + trait_def.expand_ext(cx, mitem, item, push, is_shallow) +} + +fn cs_clone_shallow(name: &str, + cx: &mut ExtCtxt, + trait_span: Span, + substr: &Substructure, + is_union: bool) + -> P { + fn assert_ty_bounds(cx: &mut ExtCtxt, stmts: &mut Vec, + ty: P, span: Span, helper_name: &str) { + // Generate statement `let _: helper_name;`, + // set the expn ID so we can use the unstable struct. + let span = super::allow_unstable(cx, span, "derive(Clone)"); + let assert_path = cx.path_all(span, true, + cx.std_path(&["clone", helper_name]), + vec![], vec![ty], vec![]); + let local = P(ast::Local { + pat: cx.pat_wild(span), + ty: Some(cx.ty_path(assert_path)), + init: None, + id: ast::DUMMY_NODE_ID, + span: span, + attrs: ast::ThinVec::new(), + }); + let stmt = ast::Stmt { + id: ast::DUMMY_NODE_ID, + node: ast::StmtKind::Local(local), + span: span, + }; + stmts.push(stmt); + } + fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec, variant: &VariantData) { + for field in variant.fields() { + // let _: AssertParamIsClone; + assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone"); + } + } + + let mut stmts = Vec::new(); + if is_union { + // let _: AssertParamIsCopy; + let self_ty = cx.ty_path(cx.path_ident(trait_span, keywords::SelfType.ident())); + assert_ty_bounds(cx, &mut stmts, self_ty, trait_span, "AssertParamIsCopy"); + } else { + match *substr.fields { + StaticStruct(vdata, ..) => { + process_variant(cx, &mut stmts, vdata); + } + StaticEnum(enum_def, ..) => { + for variant in &enum_def.variants { + process_variant(cx, &mut stmts, &variant.node.data); + } + } + _ => cx.span_bug(trait_span, &format!("unexpected substructure in \ + shallow `derive({})`", name)) + } + } + stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span)))); + cx.expr_block(cx.block(trait_span, stmts)) } fn cs_clone(name: &str, cx: &mut ExtCtxt, trait_span: Span, - substr: &Substructure, - mode: Mode) + substr: &Substructure) -> P { let ctor_path; let all_fields; - let fn_path = match mode { - Mode::Shallow => cx.std_path(&["clone", "assert_receiver_is_clone"]), - Mode::Deep => cx.std_path(&["clone", "Clone", "clone"]), - }; + let fn_path = cx.std_path(&["clone", "Clone", "clone"]); let subcall = |cx: &mut ExtCtxt, field: &FieldInfo| { let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; - - let span = if mode == Mode::Shallow { - // set the expn ID so we can call the unstable method - super::allow_unstable(cx, field.span, "derive(Clone)") - } else { - field.span - }; - cx.expr_call_global(span, fn_path.clone(), args) + cx.expr_call_global(field.span, fn_path.clone(), args) }; let vdata; @@ -145,43 +196,31 @@ fn cs_clone(name: &str, } } - match mode { - Mode::Shallow => { - let mut stmts = all_fields.iter().map(|f| { - let call = subcall(cx, f); - cx.stmt_expr(call) - }).collect::>(); - stmts.push(cx.stmt_expr(cx.expr_deref(trait_span, cx.expr_self(trait_span)))); - cx.expr_block(cx.block(trait_span, stmts)) - } - Mode::Deep => { - match *vdata { - VariantData::Struct(..) => { - let fields = all_fields.iter() - .map(|field| { - let ident = match field.name { - Some(i) => i, - None => { - cx.span_bug(trait_span, - &format!("unnamed field in normal struct in \ - `derive({})`", - name)) - } - }; - let call = subcall(cx, field); - cx.field_imm(field.span, ident, call) - }) - .collect::>(); + match *vdata { + VariantData::Struct(..) => { + let fields = all_fields.iter() + .map(|field| { + let ident = match field.name { + Some(i) => i, + None => { + cx.span_bug(trait_span, + &format!("unnamed field in normal struct in \ + `derive({})`", + name)) + } + }; + let call = subcall(cx, field); + cx.field_imm(field.span, ident, call) + }) + .collect::>(); - cx.expr_struct(trait_span, ctor_path, fields) - } - VariantData::Tuple(..) => { - let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); - let path = cx.expr_path(ctor_path); - cx.expr_call(trait_span, path, subcalls) - } - VariantData::Unit(..) => cx.expr_path(ctor_path), - } + cx.expr_struct(trait_span, ctor_path, fields) + } + VariantData::Tuple(..) => { + let subcalls = all_fields.iter().map(|f| subcall(cx, f)).collect(); + let path = cx.expr_path(ctor_path); + cx.expr_call(trait_span, path, subcalls) } + VariantData::Unit(..) => cx.expr_path(ctor_path), } } diff --git a/src/libsyntax_ext/deriving/generic/mod.rs b/src/libsyntax_ext/deriving/generic/mod.rs index 600f5d335c4..339a6c477cc 100644 --- a/src/libsyntax_ext/deriving/generic/mod.rs +++ b/src/libsyntax_ext/deriving/generic/mod.rs @@ -401,18 +401,29 @@ impl<'a> TraitDef<'a> { mitem: &ast::MetaItem, item: &'a Annotatable, push: &mut FnMut(Annotatable)) { + self.expand_ext(cx, mitem, item, push, false); + } + + pub fn expand_ext(&self, + cx: &mut ExtCtxt, + mitem: &ast::MetaItem, + item: &'a Annotatable, + push: &mut FnMut(Annotatable), + from_scratch: bool) { match *item { Annotatable::Item(ref item) => { let newitem = match item.node { ast::ItemKind::Struct(ref struct_def, ref generics) => { - self.expand_struct_def(cx, &struct_def, item.ident, generics) + self.expand_struct_def(cx, &struct_def, item.ident, generics, from_scratch) } ast::ItemKind::Enum(ref enum_def, ref generics) => { - self.expand_enum_def(cx, enum_def, &item.attrs, item.ident, generics) + self.expand_enum_def(cx, enum_def, &item.attrs, + item.ident, generics, from_scratch) } ast::ItemKind::Union(ref struct_def, ref generics) => { if self.supports_unions { - self.expand_struct_def(cx, &struct_def, item.ident, generics) + self.expand_struct_def(cx, &struct_def, item.ident, + generics, from_scratch) } else { cx.span_err(mitem.span, "this trait cannot be derived for unions"); @@ -661,7 +672,8 @@ impl<'a> TraitDef<'a> { cx: &mut ExtCtxt, struct_def: &'a VariantData, type_ident: Ident, - generics: &Generics) + generics: &Generics, + from_scratch: bool) -> P { let field_tys: Vec> = struct_def.fields() .iter() @@ -674,7 +686,7 @@ impl<'a> TraitDef<'a> { let (explicit_self, self_args, nonself_args, tys) = method_def.split_self_nonself_args(cx, self, type_ident, generics); - let body = if method_def.is_static() { + let body = if from_scratch || method_def.is_static() { method_def.expand_static_struct_method_body(cx, self, struct_def, @@ -709,7 +721,8 @@ impl<'a> TraitDef<'a> { enum_def: &'a EnumDef, type_attrs: &[ast::Attribute], type_ident: Ident, - generics: &Generics) + generics: &Generics, + from_scratch: bool) -> P { let mut field_tys = Vec::new(); @@ -727,7 +740,7 @@ impl<'a> TraitDef<'a> { let (explicit_self, self_args, nonself_args, tys) = method_def.split_self_nonself_args(cx, self, type_ident, generics); - let body = if method_def.is_static() { + let body = if from_scratch || method_def.is_static() { method_def.expand_static_enum_method_body(cx, self, enum_def, -- cgit 1.4.1-3-g733a5 From 62cb7510ac6285c93ec691198a92f910582d31a2 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 26 Aug 2016 19:23:42 +0300 Subject: Improve `Eq` deriving --- src/libcore/cmp.rs | 13 +++++- src/libsyntax/ext/build.rs | 18 +++++++++ src/libsyntax_ext/deriving/clone.rs | 15 +------ src/libsyntax_ext/deriving/cmp/eq.rs | 55 +++++++++++++++++--------- src/test/compile-fail/union/union-derive-eq.rs | 30 ++++++++++++++ src/test/compile-fail/union/union-derive.rs | 1 - src/test/run-pass/union/union-derive.rs | 13 +++++- 7 files changed, 109 insertions(+), 36 deletions(-) create mode 100644 src/test/compile-fail/union/union-derive-eq.rs (limited to 'src/libsyntax_ext') diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 670978a2d49..f990a27e52b 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -129,7 +129,7 @@ pub trait PartialEq { /// This trait can be used with `#[derive]`. When `derive`d, because `Eq` has /// no extra methods, it is only informing the compiler that this is an /// equivalence relation rather than a partial equivalence relation. Note that -/// the `derive` strategy requires all fields are `PartialEq`, which isn't +/// the `derive` strategy requires all fields are `Eq`, which isn't /// always desired. /// /// ## How can I implement `Eq`? @@ -165,6 +165,17 @@ pub trait Eq: PartialEq { fn assert_receiver_is_total_eq(&self) {} } +// FIXME: this struct is used solely by #[derive] to +// assert that every component of a type implements Eq. +// +// This struct should never appear in user code. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +#[unstable(feature = "derive_eq", + reason = "deriving hack, should not be public", + issue = "0")] +pub struct AssertParamIsEq { _field: ::marker::PhantomData } + /// An `Ordering` is the result of a comparison between two values. /// /// # Examples diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 3dcdbc89096..b81d95a6998 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -97,6 +97,7 @@ pub trait AstBuilder { typ: P, ex: P) -> P; + fn stmt_let_type_only(&self, span: Span, ty: P) -> ast::Stmt; fn stmt_item(&self, sp: Span, item: P) -> ast::Stmt; // blocks @@ -577,6 +578,23 @@ impl<'a> AstBuilder for ExtCtxt<'a> { }) } + // Generate `let _: Type;`, usually used for type assertions. + fn stmt_let_type_only(&self, span: Span, ty: P) -> ast::Stmt { + let local = P(ast::Local { + pat: self.pat_wild(span), + ty: Some(ty), + init: None, + id: ast::DUMMY_NODE_ID, + span: span, + attrs: ast::ThinVec::new(), + }); + ast::Stmt { + id: ast::DUMMY_NODE_ID, + node: ast::StmtKind::Local(local), + span: span, + } + } + fn stmt_item(&self, sp: Span, item: P) -> ast::Stmt { ast::Stmt { id: ast::DUMMY_NODE_ID, diff --git a/src/libsyntax_ext/deriving/clone.rs b/src/libsyntax_ext/deriving/clone.rs index aa7c2c301dd..d7bc2a6faee 100644 --- a/src/libsyntax_ext/deriving/clone.rs +++ b/src/libsyntax_ext/deriving/clone.rs @@ -115,20 +115,7 @@ fn cs_clone_shallow(name: &str, let assert_path = cx.path_all(span, true, cx.std_path(&["clone", helper_name]), vec![], vec![ty], vec![]); - let local = P(ast::Local { - pat: cx.pat_wild(span), - ty: Some(cx.ty_path(assert_path)), - init: None, - id: ast::DUMMY_NODE_ID, - span: span, - attrs: ast::ThinVec::new(), - }); - let stmt = ast::Stmt { - id: ast::DUMMY_NODE_ID, - node: ast::StmtKind::Local(local), - span: span, - }; - stmts.push(stmt); + stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); } fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec, variant: &VariantData) { for field in variant.fields() { diff --git a/src/libsyntax_ext/deriving/cmp/eq.rs b/src/libsyntax_ext/deriving/cmp/eq.rs index 425a47a991b..fa0fb2492c5 100644 --- a/src/libsyntax_ext/deriving/cmp/eq.rs +++ b/src/libsyntax_ext/deriving/cmp/eq.rs @@ -11,7 +11,7 @@ use deriving::generic::*; use deriving::generic::ty::*; -use syntax::ast::{Expr, MetaItem}; +use syntax::ast::{self, Expr, MetaItem}; use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; use syntax::parse::token::InternedString; @@ -23,22 +23,6 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, mitem: &MetaItem, item: &Annotatable, push: &mut FnMut(Annotatable)) { - fn cs_total_eq_assert(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P { - cs_same_method(|cx, span, exprs| { - // create `a.(); b.(); c.(); ...` - // (where method is `assert_receiver_is_total_eq`) - let stmts = exprs.into_iter().map(|e| cx.stmt_expr(e)).collect(); - let block = cx.block(span, stmts); - cx.expr_block(block) - }, - Box::new(|cx, sp, _, _| { - cx.span_bug(sp, "non matching enums in derive(Eq)?") - }), - cx, - span, - substr) - } - let inline = cx.meta_word(span, InternedString::new("inline")); let hidden = cx.meta_list_item_word(span, InternedString::new("hidden")); let doc = cx.meta_list(span, InternedString::new("doc"), vec![hidden]); @@ -50,7 +34,7 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, additional_bounds: Vec::new(), generics: LifetimeBounds::empty(), is_unsafe: false, - supports_unions: false, + supports_unions: true, methods: vec![MethodDef { name: "assert_receiver_is_total_eq", generics: LifetimeBounds::empty(), @@ -66,5 +50,38 @@ pub fn expand_deriving_eq(cx: &mut ExtCtxt, }], associated_types: Vec::new(), }; - trait_def.expand(cx, mitem, item, push) + trait_def.expand_ext(cx, mitem, item, push, true) +} + +fn cs_total_eq_assert(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P { + fn assert_ty_bounds(cx: &mut ExtCtxt, stmts: &mut Vec, + ty: P, span: Span, helper_name: &str) { + // Generate statement `let _: helper_name;`, + // set the expn ID so we can use the unstable struct. + let span = super::allow_unstable(cx, span, "derive(Eq)"); + let assert_path = cx.path_all(span, true, + cx.std_path(&["cmp", helper_name]), + vec![], vec![ty], vec![]); + stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); + } + fn process_variant(cx: &mut ExtCtxt, stmts: &mut Vec, variant: &ast::VariantData) { + for field in variant.fields() { + // let _: AssertParamIsEq; + assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsEq"); + } + } + + let mut stmts = Vec::new(); + match *substr.fields { + StaticStruct(vdata, ..) => { + process_variant(cx, &mut stmts, vdata); + } + StaticEnum(enum_def, ..) => { + for variant in &enum_def.variants { + process_variant(cx, &mut stmts, &variant.node.data); + } + } + _ => cx.span_bug(trait_span, "unexpected substructure in `derive(Eq)`") + } + cx.expr_block(cx.block(trait_span, stmts)) } diff --git a/src/test/compile-fail/union/union-derive-eq.rs b/src/test/compile-fail/union/union-derive-eq.rs new file mode 100644 index 00000000000..9dfec288c15 --- /dev/null +++ b/src/test/compile-fail/union/union-derive-eq.rs @@ -0,0 +1,30 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(untagged_unions)] + +#[derive(Eq)] // OK +union U1 { + a: u8, +} + +impl PartialEq for U1 { fn eq(&self, rhs: &Self) -> bool { true } } + +#[derive(PartialEq)] +struct PartialEqNotEq; + +#[derive(Eq)] +union U2 { + a: PartialEqNotEq, //~ ERROR the trait bound `PartialEqNotEq: std::cmp::Eq` is not satisfied +} + +impl PartialEq for U2 { fn eq(&self, rhs: &Self) -> bool { true } } + +fn main() {} diff --git a/src/test/compile-fail/union/union-derive.rs b/src/test/compile-fail/union/union-derive.rs index 01ce9696284..26dbdfd0b41 100644 --- a/src/test/compile-fail/union/union-derive.rs +++ b/src/test/compile-fail/union/union-derive.rs @@ -14,7 +14,6 @@ #[derive( PartialEq, //~ ERROR this trait cannot be derived for unions - Eq, //~ ERROR this trait cannot be derived for unions PartialOrd, //~ ERROR this trait cannot be derived for unions Ord, //~ ERROR this trait cannot be derived for unions Hash, //~ ERROR this trait cannot be derived for unions diff --git a/src/test/run-pass/union/union-derive.rs b/src/test/run-pass/union/union-derive.rs index 8314416e2b1..8ff6f17394f 100644 --- a/src/test/run-pass/union/union-derive.rs +++ b/src/test/run-pass/union/union-derive.rs @@ -15,22 +15,33 @@ #[derive( Copy, Clone, + Eq, )] union U { a: u8, b: u16, } -#[derive(Clone, Copy)] +impl PartialEq for U { fn eq(&self, rhs: &Self) -> bool { true } } + +#[derive( + Clone, + Copy, + Eq +)] union W { a: T, } +impl PartialEq for W { fn eq(&self, rhs: &Self) -> bool { true } } + fn main() { let u = U { b: 0 }; let u1 = u; let u2 = u.clone(); + assert!(u1 == u2); let w = W { a: 0 }; let w1 = w.clone(); + assert!(w == w1); } -- cgit 1.4.1-3-g733a5 From 50f94f6c95c944f08c4af264f48260e42efefd47 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Fri, 2 Sep 2016 22:01:35 +0000 Subject: Avoid needless reexpansions. --- src/libsyntax/ext/base.rs | 15 ++++++++++----- src/libsyntax_ext/format.rs | 19 ++++++++----------- 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'src/libsyntax_ext') diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index edd38ea23e2..f6eb6f1da4f 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -13,7 +13,7 @@ pub use self::SyntaxExtension::*; use ast; use ast::{Name, PatKind}; use attr::HasAttrs; -use codemap::{self, CodeMap, ExpnInfo}; +use codemap::{self, CodeMap, ExpnInfo, Spanned, respan}; use syntax_pos::{Span, ExpnId, NO_EXPANSION}; use errors::DiagnosticBuilder; use ext; @@ -805,8 +805,8 @@ impl<'a> ExtCtxt<'a> { /// Extract a string literal from the macro expanded version of `expr`, /// emitting `err_msg` if `expr` is not a string literal. This does not stop /// compilation on error, merely emits a non-fatal error and returns None. -pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) - -> Option<(InternedString, ast::StrStyle)> { +pub fn expr_to_spanned_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) + -> Option> { // Update `expr.span`'s expn_id now in case expr is an `include!` macro invocation. let expr = expr.map(|mut expr| { expr.span.expn_id = cx.backtrace; @@ -817,7 +817,7 @@ pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) let expr = cx.expander().fold_expr(expr); match expr.node { ast::ExprKind::Lit(ref l) => match l.node { - ast::LitKind::Str(ref s, style) => return Some(((*s).clone(), style)), + ast::LitKind::Str(ref s, style) => return Some(respan(expr.span, (s.clone(), style))), _ => cx.span_err(l.span, err_msg) }, _ => cx.span_err(expr.span, err_msg) @@ -825,6 +825,11 @@ pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) None } +pub fn expr_to_string(cx: &mut ExtCtxt, expr: P, err_msg: &str) + -> Option<(InternedString, ast::StrStyle)> { + expr_to_spanned_string(cx, expr, err_msg).map(|s| s.node) +} + /// Non-fatally assert that `tts` is empty. Note that this function /// returns even when `tts` is non-empty, macros that *need* to stop /// compilation should call @@ -851,7 +856,7 @@ pub fn get_single_str_from_tts(cx: &mut ExtCtxt, cx.span_err(sp, &format!("{} takes 1 argument", name)); return None } - let ret = cx.expander().fold_expr(panictry!(p.parse_expr())); + let ret = panictry!(p.parse_expr()); if p.token != token::Eof { cx.span_err(sp, &format!("{} takes 1 argument", name)); } diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 06b16095d19..892ebcfa761 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -17,7 +17,6 @@ use syntax::ast; use syntax::ext::base::*; use syntax::ext::base; use syntax::ext::build::AstBuilder; -use syntax::fold::Folder; use syntax::parse::token::{self, keywords}; use syntax::ptr::P; use syntax_pos::{Span, DUMMY_SP}; @@ -702,10 +701,12 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect(); let arg_unique_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect(); let macsp = ecx.call_site(); - // Expand the format literal so that efmt.span will have a backtrace. This - // is essential for locating a bug when the format literal is generated in - // a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")). - let efmt = ecx.expander().fold_expr(efmt); + let msg = "format argument must be a string literal."; + let fmt = match expr_to_spanned_string(ecx, efmt, msg) { + Some(fmt) => fmt, + None => return DummyResult::raw_expr(sp), + }; + let mut cx = Context { ecx: ecx, args: args, @@ -723,14 +724,10 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, str_pieces: Vec::new(), all_pieces_simple: true, macsp: macsp, - fmtsp: efmt.span, - }; - let fmt = match expr_to_string(cx.ecx, efmt, "format argument must be a string literal.") { - Some((fmt, _)) => fmt, - None => return DummyResult::raw_expr(sp), + fmtsp: fmt.span, }; - let mut parser = parse::Parser::new(&fmt); + let mut parser = parse::Parser::new(&fmt.node.0); let mut pieces = vec![]; loop { -- cgit 1.4.1-3-g733a5 From a9821e1658240bb2c056f260a4b6bc9789301fae Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Mon, 5 Sep 2016 03:46:05 +0000 Subject: Refactor `ExtCtxt` to use a `Resolver` instead of a `MacroLoader`. --- src/librustc/middle/cstore.rs | 4 ++++ src/librustc_driver/driver.rs | 15 +++++++-------- src/librustc_metadata/macro_import.rs | 4 ++-- src/librustc_resolve/lib.rs | 18 +++++++++++++++++- src/libsyntax/ext/base.rs | 12 ++++++------ src/libsyntax/ext/expand.rs | 12 ++++++------ src/libsyntax/test.rs | 6 +++--- src/libsyntax_ext/rustc_macro_registrar.rs | 4 ++-- src/test/compile-fail-fulldeps/qquote.rs | 4 ++-- src/test/run-fail-fulldeps/qquote.rs | 4 ++-- src/test/run-pass-fulldeps/qquote.rs | 4 ++-- 11 files changed, 53 insertions(+), 34 deletions(-) (limited to 'src/libsyntax_ext') diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index b33bc520fe2..5bbc0cd9d90 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -39,6 +39,7 @@ use std::rc::Rc; use std::path::PathBuf; use syntax::ast; use syntax::attr; +use syntax::ext::base::LoadedMacro; use syntax::ptr::P; use syntax::parse::token::InternedString; use syntax_pos::Span; @@ -488,6 +489,9 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { fn metadata_encoding_version(&self) -> &[u8] { bug!("metadata_encoding_version") } } +pub trait MacroLoader { + fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec; +} /// Metadata encoding and decoding can make use of thread-local encoding and /// decoding contexts. These allow implementers of serialize::Encodable and diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index ba6c4b9b84c..0d9bf14f12f 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -638,6 +638,12 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, } sess.track_errors(|| sess.lint_store.borrow_mut().process_command_line(sess))?; + let mut macro_loader = + macro_import::MacroLoader::new(sess, &cstore, crate_name, krate.config.clone()); + + let resolver_arenas = Resolver::arenas(); + let mut resolver = Resolver::new(sess, make_glob_map, &mut macro_loader, &resolver_arenas); + krate = time(time_passes, "expansion", || { // Windows dlls do not have rpaths, so they don't know how to find their // dependencies. It's up to us to tell the system where to find all the @@ -672,14 +678,10 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, trace_mac: sess.opts.debugging_opts.trace_macros, should_test: sess.opts.test, }; - let mut loader = macro_import::MacroLoader::new(sess, - &cstore, - crate_name, - krate.config.clone()); let mut ecx = syntax::ext::base::ExtCtxt::new(&sess.parse_sess, krate.config.clone(), cfg, - &mut loader); + &mut resolver); syntax_ext::register_builtins(&mut ecx.syntax_env); let ret = syntax::ext::expand::expand_crate(&mut ecx, syntax_exts, krate); if cfg!(windows) { @@ -708,9 +710,6 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, &sess.features.borrow()) }); - let resolver_arenas = Resolver::arenas(); - let mut resolver = Resolver::new(sess, make_glob_map, &resolver_arenas); - let krate = time(sess.time_passes(), "assigning node ids", || resolver.assign_node_ids(krate)); if sess.opts.debugging_opts.input_stats { diff --git a/src/librustc_metadata/macro_import.rs b/src/librustc_metadata/macro_import.rs index 22691975050..e41f076d64a 100644 --- a/src/librustc_metadata/macro_import.rs +++ b/src/librustc_metadata/macro_import.rs @@ -18,6 +18,7 @@ use creader::{CrateReader, Macros}; use cstore::CStore; use rustc::hir::def_id::DefIndex; +use rustc::middle; use rustc::session::Session; use rustc::util::nodemap::FnvHashMap; use rustc_back::dynamic_lib::DynamicLibrary; @@ -26,7 +27,6 @@ use rustc_macro::__internal::Registry; use syntax::ast; use syntax::attr; use syntax::ext::base::LoadedMacro; -use syntax::ext; use syntax::parse::token; use syntax_ext::deriving::custom::CustomDerive; use syntax_pos::Span; @@ -55,7 +55,7 @@ pub fn call_bad_macro_reexport(a: &Session, b: Span) { pub type MacroSelection = FnvHashMap; -impl<'a> ext::base::MacroLoader for MacroLoader<'a> { +impl<'a> middle::cstore::MacroLoader for MacroLoader<'a> { fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index c5b505fba38..c1e6d93a970 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -45,6 +45,7 @@ use self::ParentLink::*; use rustc::hir::map::Definitions; use rustc::hir::{self, PrimTy, TyBool, TyChar, TyFloat, TyInt, TyUint, TyStr}; +use rustc::middle::cstore::MacroLoader; use rustc::session::Session; use rustc::lint; use rustc::hir::def::*; @@ -53,6 +54,8 @@ use rustc::ty; use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, NodeSet, FnvHashMap, FnvHashSet}; +use syntax::ext; +use syntax::ext::base::LoadedMacro; use syntax::ext::hygiene::Mark; use syntax::ast::{self, FloatTy}; use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, IntTy, UintTy}; @@ -1068,6 +1071,8 @@ pub struct Resolver<'a> { arenas: &'a ResolverArenas<'a>, dummy_binding: &'a NameBinding<'a>, new_import_semantics: bool, // true if `#![feature(item_like_imports)]` + + macro_loader: &'a mut MacroLoader, } pub struct ResolverArenas<'a> { @@ -1149,6 +1154,12 @@ impl<'a> hir::lowering::Resolver for Resolver<'a> { } } +impl<'a> ext::base::Resolver for Resolver<'a> { + fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec { + self.macro_loader.load_crate(extern_crate, allows_macros) + } +} + trait Named { fn name(&self) -> Name; } @@ -1166,7 +1177,10 @@ impl Named for hir::PathSegment { } impl<'a> Resolver<'a> { - pub fn new(session: &'a Session, make_glob_map: MakeGlobMap, arenas: &'a ResolverArenas<'a>) + pub fn new(session: &'a Session, + make_glob_map: MakeGlobMap, + macro_loader: &'a mut MacroLoader, + arenas: &'a ResolverArenas<'a>) -> Resolver<'a> { let root_def_id = DefId::local(CRATE_DEF_INDEX); let graph_root = @@ -1227,6 +1241,8 @@ impl<'a> Resolver<'a> { vis: ty::Visibility::Public, }), new_import_semantics: session.features.borrow().item_like_imports, + + macro_loader: macro_loader, } } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index f6eb6f1da4f..d0e11643c64 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -546,7 +546,7 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>) syntax_expanders } -pub trait MacroLoader { +pub trait Resolver { fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec; } @@ -556,8 +556,8 @@ pub enum LoadedMacro { CustomDerive(String, Box), } -pub struct DummyMacroLoader; -impl MacroLoader for DummyMacroLoader { +pub struct DummyResolver; +impl Resolver for DummyResolver { fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec { Vec::new() } @@ -572,7 +572,7 @@ pub struct ExtCtxt<'a> { pub backtrace: ExpnId, pub ecfg: expand::ExpansionConfig<'a>, pub crate_root: Option<&'static str>, - pub loader: &'a mut MacroLoader, + pub resolver: &'a mut Resolver, pub exported_macros: Vec, @@ -584,7 +584,7 @@ pub struct ExtCtxt<'a> { impl<'a> ExtCtxt<'a> { pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig, ecfg: expand::ExpansionConfig<'a>, - loader: &'a mut MacroLoader) + resolver: &'a mut Resolver) -> ExtCtxt<'a> { ExtCtxt { syntax_env: initial_syntax_expander_table(&ecfg), @@ -594,7 +594,7 @@ impl<'a> ExtCtxt<'a> { ecfg: ecfg, crate_root: None, exported_macros: Vec::new(), - loader: loader, + resolver: resolver, derive_modes: HashMap::new(), recursion_count: 0, } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 4715eda8374..44db1dd17ae 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -644,7 +644,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { // We need to error on `#[macro_use] extern crate` when it isn't at the // crate root, because `$crate` won't work properly. let is_crate_root = self.cx.syntax_env.is_crate_root(); - for def in self.cx.loader.load_crate(&*item, is_crate_root) { + for def in self.cx.resolver.load_crate(&*item, is_crate_root) { match def { LoadedMacro::Def(def) => self.cx.insert_macro(def), LoadedMacro::CustomDerive(name, ext) => { @@ -809,7 +809,7 @@ fn mark_tts(tts: &[TokenTree], m: Mark) -> Vec { mod tests { use super::{expand_crate, ExpansionConfig}; use ast; - use ext::base::{ExtCtxt, DummyMacroLoader}; + use ext::base::{ExtCtxt, DummyResolver}; use parse; use util::parser_testing::{string_to_parser}; use visit; @@ -850,7 +850,7 @@ mod tests { src, Vec::new(), &sess).unwrap(); // should fail: - let mut loader = DummyMacroLoader; + let mut loader = DummyResolver; let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader); expand_crate(&mut ecx, vec![], crate_ast); } @@ -865,7 +865,7 @@ mod tests { "".to_string(), src, Vec::new(), &sess).unwrap(); - let mut loader = DummyMacroLoader; + let mut loader = DummyResolver; let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader); expand_crate(&mut ecx, vec![], crate_ast); } @@ -879,7 +879,7 @@ mod tests { "".to_string(), src, Vec::new(), &sess).unwrap(); - let mut loader = DummyMacroLoader; + let mut loader = DummyResolver; let mut ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader); expand_crate(&mut ecx, vec![], crate_ast); } @@ -888,7 +888,7 @@ mod tests { let ps = parse::ParseSess::new(); let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod()); // the cfg argument actually does matter, here... - let mut loader = DummyMacroLoader; + let mut loader = DummyResolver; let mut ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader); expand_crate(&mut ecx, vec![], crate_ast) } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 3108296e778..dde8a8d271f 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -28,7 +28,7 @@ use errors; use errors::snippet::{SnippetData}; use config; use entry::{self, EntryPointType}; -use ext::base::{ExtCtxt, DummyMacroLoader}; +use ext::base::{ExtCtxt, DummyResolver}; use ext::build::AstBuilder; use ext::expand::ExpansionConfig; use fold::Folder; @@ -276,13 +276,13 @@ fn generate_test_harness(sess: &ParseSess, let mut cleaner = EntryPointCleaner { depth: 0 }; let krate = cleaner.fold_crate(krate); - let mut loader = DummyMacroLoader; + let mut resolver = DummyResolver; let mut cx: TestCtxt = TestCtxt { sess: sess, span_diagnostic: sd, ext_cx: ExtCtxt::new(sess, vec![], ExpansionConfig::default("test".to_string()), - &mut loader), + &mut resolver), path: Vec::new(), testfns: Vec::new(), reexport_test_harness_main: reexport_test_harness_main, diff --git a/src/libsyntax_ext/rustc_macro_registrar.rs b/src/libsyntax_ext/rustc_macro_registrar.rs index 7693e2416f4..78fed9d33dd 100644 --- a/src/libsyntax_ext/rustc_macro_registrar.rs +++ b/src/libsyntax_ext/rustc_macro_registrar.rs @@ -13,7 +13,7 @@ use std::mem; use errors; use syntax::ast::{self, Ident, NodeId}; use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute}; -use syntax::ext::base::{ExtCtxt, DummyMacroLoader}; +use syntax::ext::base::{ExtCtxt, DummyResolver}; use syntax::ext::build::AstBuilder; use syntax::ext::expand::ExpansionConfig; use syntax::parse::ParseSess; @@ -44,7 +44,7 @@ pub fn modify(sess: &ParseSess, num_crate_types: usize, handler: &errors::Handler, features: &Features) -> ast::Crate { - let mut loader = DummyMacroLoader; + let mut loader = DummyResolver; let mut cx = ExtCtxt::new(sess, Vec::new(), ExpansionConfig::default("rustc_macro".to_string()), diff --git a/src/test/compile-fail-fulldeps/qquote.rs b/src/test/compile-fail-fulldeps/qquote.rs index e29ded8a052..3e5d17e2ffb 100644 --- a/src/test/compile-fail-fulldeps/qquote.rs +++ b/src/test/compile-fail-fulldeps/qquote.rs @@ -22,11 +22,11 @@ use syntax_pos::DUMMY_SP; fn main() { let ps = syntax::parse::ParseSess::new(); - let mut loader = syntax::ext::base::DummyMacroLoader; + let mut resolver = syntax::ext::base::DummyResolver; let mut cx = syntax::ext::base::ExtCtxt::new( &ps, vec![], syntax::ext::expand::ExpansionConfig::default("qquote".to_string()), - &mut loader); + &mut resolver); cx.bt_push(syntax::codemap::ExpnInfo { call_site: DUMMY_SP, callee: syntax::codemap::NameAndSpan { diff --git a/src/test/run-fail-fulldeps/qquote.rs b/src/test/run-fail-fulldeps/qquote.rs index 47e97abbbaa..1458583ff58 100644 --- a/src/test/run-fail-fulldeps/qquote.rs +++ b/src/test/run-fail-fulldeps/qquote.rs @@ -25,11 +25,11 @@ use syntax_pos::DUMMY_SP; fn main() { let ps = syntax::parse::ParseSess::new(); - let mut loader = syntax::ext::base::DummyMacroLoader; + let mut resolver = syntax::ext::base::DummyResolver; let mut cx = syntax::ext::base::ExtCtxt::new( &ps, vec![], syntax::ext::expand::ExpansionConfig::default("qquote".to_string()), - &mut loader); + &mut resolver); cx.bt_push(syntax::codemap::ExpnInfo { call_site: DUMMY_SP, callee: syntax::codemap::NameAndSpan { diff --git a/src/test/run-pass-fulldeps/qquote.rs b/src/test/run-pass-fulldeps/qquote.rs index a4f0e35cc5a..2a53a62a5ab 100644 --- a/src/test/run-pass-fulldeps/qquote.rs +++ b/src/test/run-pass-fulldeps/qquote.rs @@ -21,11 +21,11 @@ use syntax_pos::DUMMY_SP; fn main() { let ps = syntax::parse::ParseSess::new(); - let mut loader = syntax::ext::base::DummyMacroLoader; + let mut resolver = syntax::ext::base::DummyResolver; let mut cx = syntax::ext::base::ExtCtxt::new( &ps, vec![], syntax::ext::expand::ExpansionConfig::default("qquote".to_string()), - &mut loader); + &mut resolver); cx.bt_push(syntax::codemap::ExpnInfo { call_site: DUMMY_SP, callee: syntax::codemap::NameAndSpan { -- cgit 1.4.1-3-g733a5 From 72a636975fc5d0bb4af45af7bdd97987cc722a6a Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 7 Sep 2016 23:21:59 +0000 Subject: Move macro resolution into `librustc_resolve`. --- src/librustc/session/mod.rs | 9 +- src/librustc_driver/driver.rs | 9 +- src/librustc_resolve/lib.rs | 18 +-- src/librustc_resolve/macros.rs | 213 ++++++++++++++++++++++++++ src/libsyntax/ext/base.rs | 298 ++++++++---------------------------- src/libsyntax/ext/expand.rs | 182 ++++++++++------------ src/libsyntax/ext/hygiene.rs | 11 +- src/libsyntax/ext/source_util.rs | 4 +- src/libsyntax/ext/tt/macro_rules.rs | 4 +- src/libsyntax_ext/deriving/mod.rs | 9 +- src/libsyntax_ext/lib.rs | 88 +++++++---- 11 files changed, 439 insertions(+), 406 deletions(-) create mode 100644 src/librustc_resolve/macros.rs (limited to 'src/libsyntax_ext') diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 3477ec6f99a..49686d63ee4 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -21,7 +21,7 @@ use util::nodemap::{NodeMap, FnvHashMap}; use util::common::duration_to_secs_str; use mir::transform as mir_pass; -use syntax::ast::{NodeId, Name}; +use syntax::ast::NodeId; use errors::{self, DiagnosticBuilder}; use errors::emitter::{Emitter, EmitterWriter}; use syntax::json::JsonEmitter; @@ -39,7 +39,7 @@ use llvm; use std::path::{Path, PathBuf}; use std::cell::{self, Cell, RefCell}; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::env; use std::ffi::CString; use std::rc::Rc; @@ -96,10 +96,6 @@ pub struct Session { pub injected_allocator: Cell>, pub injected_panic_runtime: Cell>, - /// Names of all bang-style macros and syntax extensions - /// available in this crate - pub available_macros: RefCell>, - /// Map from imported macro spans (which consist of /// the localized span for the macro body) to the /// macro name and defintion span in the source crate. @@ -552,7 +548,6 @@ pub fn build_session_(sopts: config::Options, next_node_id: Cell::new(1), injected_allocator: Cell::new(None), injected_panic_runtime: Cell::new(None), - available_macros: RefCell::new(HashSet::new()), imported_macro_spans: RefCell::new(HashMap::new()), incr_comp_session: RefCell::new(IncrCompSession::NotInitialized), perf_stats: PerfStats { diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 0d9bf14f12f..bf50c96034d 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -50,6 +50,7 @@ use std::io::{self, Write}; use std::path::{Path, PathBuf}; use syntax::{ast, diagnostics, visit}; use syntax::attr; +use syntax::ext::base::ExtCtxt; use syntax::parse::{self, PResult, token}; use syntax::util::node_count::NodeCounter; use syntax; @@ -643,6 +644,7 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, let resolver_arenas = Resolver::arenas(); let mut resolver = Resolver::new(sess, make_glob_map, &mut macro_loader, &resolver_arenas); + syntax_ext::register_builtins(&mut resolver, sess.features.borrow().quote); krate = time(time_passes, "expansion", || { // Windows dlls do not have rpaths, so they don't know how to find their @@ -678,16 +680,11 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, trace_mac: sess.opts.debugging_opts.trace_macros, should_test: sess.opts.test, }; - let mut ecx = syntax::ext::base::ExtCtxt::new(&sess.parse_sess, - krate.config.clone(), - cfg, - &mut resolver); - syntax_ext::register_builtins(&mut ecx.syntax_env); + let mut ecx = ExtCtxt::new(&sess.parse_sess, krate.config.clone(), cfg, &mut resolver); let ret = syntax::ext::expand::expand_crate(&mut ecx, syntax_exts, krate); if cfg!(windows) { env::set_var("PATH", &old_path); } - *sess.available_macros.borrow_mut() = ecx.syntax_env.names; ret }); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index c1e6d93a970..6bf45ab8f6f 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -54,8 +54,6 @@ use rustc::ty; use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, NodeSet, FnvHashMap, FnvHashSet}; -use syntax::ext; -use syntax::ext::base::LoadedMacro; use syntax::ext::hygiene::Mark; use syntax::ast::{self, FloatTy}; use syntax::ast::{CRATE_NODE_ID, Name, NodeId, CrateNum, IntTy, UintTy}; @@ -82,6 +80,7 @@ use resolve_imports::{ImportDirective, NameResolution}; // registered before they are used. mod diagnostics; +mod macros; mod check_unused; mod build_reduced_graph; mod resolve_imports; @@ -1073,6 +1072,10 @@ pub struct Resolver<'a> { new_import_semantics: bool, // true if `#![feature(item_like_imports)]` macro_loader: &'a mut MacroLoader, + macro_names: FnvHashSet, + + // Maps the `Mark` of an expansion to its containing module or block. + expansion_data: Vec, } pub struct ResolverArenas<'a> { @@ -1154,12 +1157,6 @@ impl<'a> hir::lowering::Resolver for Resolver<'a> { } } -impl<'a> ext::base::Resolver for Resolver<'a> { - fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec { - self.macro_loader.load_crate(extern_crate, allows_macros) - } -} - trait Named { fn name(&self) -> Name; } @@ -1243,6 +1240,8 @@ impl<'a> Resolver<'a> { new_import_semantics: session.features.borrow().item_like_imports, macro_loader: macro_loader, + macro_names: FnvHashSet(), + expansion_data: vec![macros::ExpansionData::default()], } } @@ -2784,8 +2783,7 @@ impl<'a> Resolver<'a> { } fn find_best_match(&mut self, name: &str) -> SuggestionType { - if let Some(macro_name) = self.session.available_macros - .borrow().iter().find(|n| n.as_str() == name) { + if let Some(macro_name) = self.macro_names.iter().find(|n| n.as_str() == name) { return SuggestionType::Macro(format!("{}!", macro_name)); } diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs new file mode 100644 index 00000000000..36f501a54d2 --- /dev/null +++ b/src/librustc_resolve/macros.rs @@ -0,0 +1,213 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use Resolver; +use rustc::util::nodemap::FnvHashMap; +use std::cell::RefCell; +use std::mem; +use std::rc::Rc; +use syntax::ast::{self, Name}; +use syntax::errors::DiagnosticBuilder; +use syntax::ext::base::{self, LoadedMacro, MultiModifier, MultiDecorator}; +use syntax::ext::base::{NormalTT, SyntaxExtension}; +use syntax::ext::expand::{Expansion, Invocation, InvocationKind}; +use syntax::ext::hygiene::Mark; +use syntax::parse::token::intern; +use syntax::util::lev_distance::find_best_match_for_name; +use syntax::visit::{self, Visitor}; + +#[derive(Clone, Default)] +pub struct ExpansionData { + module: Rc, +} + +// FIXME(jseyfried): merge with `::ModuleS`. +#[derive(Default)] +struct ModuleData { + parent: Option>, + macros: RefCell>>, + macros_escape: bool, +} + +impl<'a> base::Resolver for Resolver<'a> { + fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec { + self.macro_loader.load_crate(extern_crate, allows_macros) + } + + fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) { + expansion.visit_with(&mut ExpansionVisitor { + current_module: self.expansion_data[mark.as_u32() as usize].module.clone(), + resolver: self, + }); + } + + fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc) { + if let NormalTT(..) = *ext { + self.macro_names.insert(ident.name); + } + + let mut module = self.expansion_data[scope.as_u32() as usize].module.clone(); + while module.macros_escape { + module = module.parent.clone().unwrap(); + } + module.macros.borrow_mut().insert(ident.name, ext); + } + + fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec) { + self.macros_at_scope.insert(id, macros); + } + + fn find_attr_invoc(&mut self, attrs: &mut Vec) -> Option { + for i in 0..attrs.len() { + let name = intern(&attrs[i].name()); + match self.expansion_data[0].module.macros.borrow().get(&name) { + Some(ext) => match **ext { + MultiModifier(..) | MultiDecorator(..) => return Some(attrs.remove(i)), + _ => {} + }, + None => {} + } + } + None + } + + fn resolve_invoc(&mut self, invoc: &Invocation) -> Option> { + let (name, span) = match invoc.kind { + InvocationKind::Bang { ref mac, .. } => { + let path = &mac.node.path; + if path.segments.len() > 1 || path.global || + !path.segments[0].parameters.is_empty() { + self.session.span_err(path.span, + "expected macro name without module separators"); + return None; + } + (path.segments[0].identifier.name, path.span) + } + InvocationKind::Attr { ref attr, .. } => (intern(&*attr.name()), attr.span), + }; + + let mut module = self.expansion_data[invoc.mark().as_u32() as usize].module.clone(); + loop { + if let Some(ext) = module.macros.borrow().get(&name) { + return Some(ext.clone()); + } + match module.parent.clone() { + Some(parent) => module = parent, + None => break, + } + } + + let mut err = + self.session.struct_span_err(span, &format!("macro undefined: '{}!'", name)); + self.suggest_macro_name(&name.as_str(), &mut err); + err.emit(); + None + } +} + +impl<'a> Resolver<'a> { + fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) { + if let Some(suggestion) = find_best_match_for_name(self.macro_names.iter(), name, None) { + if suggestion != name { + err.help(&format!("did you mean `{}!`?", suggestion)); + } else { + err.help(&format!("have you added the `#[macro_use]` on the module/import?")); + } + } + } +} + +struct ExpansionVisitor<'b, 'a: 'b> { + resolver: &'b mut Resolver<'a>, + current_module: Rc, +} + +impl<'a, 'b> ExpansionVisitor<'a, 'b> { + fn visit_invoc(&mut self, id: ast::NodeId) { + assert_eq!(id, self.resolver.expansion_data.len() as u32); + self.resolver.expansion_data.push(ExpansionData { + module: self.current_module.clone(), + }); + } + + // does this attribute list contain "macro_use"? + fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { + for attr in attrs { + if attr.check_name("macro_escape") { + let msg = "macro_escape is a deprecated synonym for macro_use"; + let mut err = self.resolver.session.struct_span_warn(attr.span, msg); + if let ast::AttrStyle::Inner = attr.node.style { + err.help("consider an outer attribute, #[macro_use] mod ...").emit(); + } else { + err.emit(); + } + } else if !attr.check_name("macro_use") { + continue; + } + + if !attr.is_word() { + self.resolver.session.span_err(attr.span, + "arguments to macro_use are not allowed here"); + } + return true; + } + + false + } +} + +macro_rules! method { + ($visit:ident: $ty:ty, $invoc:path, $walk:ident) => { + fn $visit(&mut self, node: &$ty) { + match node.node { + $invoc(..) => self.visit_invoc(node.id), + _ => visit::$walk(self, node), + } + } + } +} + +impl<'a, 'b> Visitor for ExpansionVisitor<'a, 'b> { + method!(visit_trait_item: ast::TraitItem, ast::TraitItemKind::Macro, walk_trait_item); + method!(visit_impl_item: ast::ImplItem, ast::ImplItemKind::Macro, walk_impl_item); + method!(visit_stmt: ast::Stmt, ast::StmtKind::Mac, walk_stmt); + method!(visit_expr: ast::Expr, ast::ExprKind::Mac, walk_expr); + method!(visit_pat: ast::Pat, ast::PatKind::Mac, walk_pat); + method!(visit_ty: ast::Ty, ast::TyKind::Mac, walk_ty); + + fn visit_item(&mut self, item: &ast::Item) { + match item.node { + ast::ItemKind::Mac(..) if item.id == ast::DUMMY_NODE_ID => {} // Scope placeholder + ast::ItemKind::Mac(..) => self.visit_invoc(item.id), + ast::ItemKind::Mod(..) => { + let module_data = ModuleData { + parent: Some(self.current_module.clone()), + macros: RefCell::new(FnvHashMap()), + macros_escape: self.contains_macro_use(&item.attrs), + }; + let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data)); + visit::walk_item(self, item); + self.current_module = orig_module; + } + _ => visit::walk_item(self, item), + } + } + + fn visit_block(&mut self, block: &ast::Block) { + let module_data = ModuleData { + parent: Some(self.current_module.clone()), + macros: RefCell::new(FnvHashMap()), + macros_escape: false, + }; + let orig_module = mem::replace(&mut self.current_module, Rc::new(module_data)); + visit::walk_block(self, block); + self.current_module = orig_module; + } +} diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index d0e11643c64..d46e2a9872e 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -10,27 +10,25 @@ pub use self::SyntaxExtension::*; -use ast; -use ast::{Name, PatKind}; +use ast::{self, Attribute, Name, PatKind}; use attr::HasAttrs; use codemap::{self, CodeMap, ExpnInfo, Spanned, respan}; use syntax_pos::{Span, ExpnId, NO_EXPANSION}; use errors::DiagnosticBuilder; -use ext; -use ext::expand; +use ext::expand::{self, Invocation, Expansion}; +use ext::hygiene::Mark; use ext::tt::macro_rules; use parse; use parse::parser; use parse::token; -use parse::token::{InternedString, intern, str_to_ident}; +use parse::token::{InternedString, str_to_ident}; use ptr::P; use std_inject; use util::small_vector::SmallVector; -use util::lev_distance::find_best_match_for_name; use fold::Folder; use feature_gate; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::path::PathBuf; use std::rc::Rc; use tokenstream; @@ -44,7 +42,7 @@ pub enum Annotatable { } impl HasAttrs for Annotatable { - fn attrs(&self) -> &[ast::Attribute] { + fn attrs(&self) -> &[Attribute] { match *self { Annotatable::Item(ref item) => &item.attrs, Annotatable::TraitItem(ref trait_item) => &trait_item.attrs, @@ -52,7 +50,7 @@ impl HasAttrs for Annotatable { } } - fn map_attrs) -> Vec>(self, f: F) -> Self { + fn map_attrs) -> Vec>(self, f: F) -> Self { match self { Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)), Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)), @@ -464,91 +462,15 @@ pub enum SyntaxExtension { pub type NamedSyntaxExtension = (Name, SyntaxExtension); -/// The base map of methods for expanding syntax extension -/// AST nodes into full ASTs -fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>) - -> SyntaxEnv { - // utility function to simplify creating NormalTT syntax extensions - fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension { - NormalTT(Box::new(f), None, false) - } - - let mut syntax_expanders = SyntaxEnv::new(); - syntax_expanders.insert(intern("macro_rules"), MacroRulesTT); - - if ecfg.enable_quotes() { - // Quasi-quoting expanders - syntax_expanders.insert(intern("quote_tokens"), - builtin_normal_expander( - ext::quote::expand_quote_tokens)); - syntax_expanders.insert(intern("quote_expr"), - builtin_normal_expander( - ext::quote::expand_quote_expr)); - syntax_expanders.insert(intern("quote_ty"), - builtin_normal_expander( - ext::quote::expand_quote_ty)); - syntax_expanders.insert(intern("quote_item"), - builtin_normal_expander( - ext::quote::expand_quote_item)); - syntax_expanders.insert(intern("quote_pat"), - builtin_normal_expander( - ext::quote::expand_quote_pat)); - syntax_expanders.insert(intern("quote_arm"), - builtin_normal_expander( - ext::quote::expand_quote_arm)); - syntax_expanders.insert(intern("quote_stmt"), - builtin_normal_expander( - ext::quote::expand_quote_stmt)); - syntax_expanders.insert(intern("quote_matcher"), - builtin_normal_expander( - ext::quote::expand_quote_matcher)); - syntax_expanders.insert(intern("quote_attr"), - builtin_normal_expander( - ext::quote::expand_quote_attr)); - syntax_expanders.insert(intern("quote_arg"), - builtin_normal_expander( - ext::quote::expand_quote_arg)); - syntax_expanders.insert(intern("quote_block"), - builtin_normal_expander( - ext::quote::expand_quote_block)); - syntax_expanders.insert(intern("quote_meta_item"), - builtin_normal_expander( - ext::quote::expand_quote_meta_item)); - syntax_expanders.insert(intern("quote_path"), - builtin_normal_expander( - ext::quote::expand_quote_path)); - } - - syntax_expanders.insert(intern("line"), - builtin_normal_expander( - ext::source_util::expand_line)); - syntax_expanders.insert(intern("column"), - builtin_normal_expander( - ext::source_util::expand_column)); - syntax_expanders.insert(intern("file"), - builtin_normal_expander( - ext::source_util::expand_file)); - syntax_expanders.insert(intern("stringify"), - builtin_normal_expander( - ext::source_util::expand_stringify)); - syntax_expanders.insert(intern("include"), - builtin_normal_expander( - ext::source_util::expand_include)); - syntax_expanders.insert(intern("include_str"), - builtin_normal_expander( - ext::source_util::expand_include_str)); - syntax_expanders.insert(intern("include_bytes"), - builtin_normal_expander( - ext::source_util::expand_include_bytes)); - syntax_expanders.insert(intern("module_path"), - builtin_normal_expander( - ext::source_util::expand_mod)); - syntax_expanders -} - pub trait Resolver { - fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) - -> Vec; + fn load_crate(&mut self, extern_crate: &ast::Item, allows_macros: bool) -> Vec; + + fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion); + fn add_macro(&mut self, scope: Mark, ident: ast::Ident, ext: Rc); + fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec); + + fn find_attr_invoc(&mut self, attrs: &mut Vec) -> Option; + fn resolve_invoc(&mut self, invoc: &Invocation) -> Option>; } pub enum LoadedMacro { @@ -558,9 +480,31 @@ pub enum LoadedMacro { pub struct DummyResolver; impl Resolver for DummyResolver { - fn load_crate(&mut self, _: &ast::Item, _: bool) -> Vec { + fn load_crate(&mut self, _extern_crate: &ast::Item, _allows_macros: bool) -> Vec { Vec::new() } + + fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion) {} + fn add_macro(&mut self, _scope: Mark, _ident: ast::Ident, _ext: Rc) {} + fn add_expansions_at_stmt(&mut self, _id: ast::NodeId, _macros: Vec) {} + + fn find_attr_invoc(&mut self, _attrs: &mut Vec) -> Option { None } + fn resolve_invoc(&mut self, _invoc: &Invocation) -> Option> { None } +} + +#[derive(Clone)] +pub struct ModuleData { + pub mod_path: Vec, + pub directory: PathBuf, +} + +#[derive(Clone)] +pub struct ExpansionData { + pub mark: Mark, + pub depth: usize, + pub backtrace: ExpnId, + pub module: Rc, + pub in_block: bool, } /// One of these is made during expansion and incrementally updated as we go; @@ -569,16 +513,12 @@ impl Resolver for DummyResolver { pub struct ExtCtxt<'a> { pub parse_sess: &'a parse::ParseSess, pub cfg: ast::CrateConfig, - pub backtrace: ExpnId, pub ecfg: expand::ExpansionConfig<'a>, pub crate_root: Option<&'static str>, pub resolver: &'a mut Resolver, - pub exported_macros: Vec, - - pub syntax_env: SyntaxEnv, pub derive_modes: HashMap>, - pub recursion_count: usize, + pub current_expansion: ExpansionData, } impl<'a> ExtCtxt<'a> { @@ -587,16 +527,20 @@ impl<'a> ExtCtxt<'a> { resolver: &'a mut Resolver) -> ExtCtxt<'a> { ExtCtxt { - syntax_env: initial_syntax_expander_table(&ecfg), parse_sess: parse_sess, cfg: cfg, - backtrace: NO_EXPANSION, ecfg: ecfg, crate_root: None, exported_macros: Vec::new(), resolver: resolver, derive_modes: HashMap::new(), - recursion_count: 0, + current_expansion: ExpansionData { + mark: Mark::root(), + depth: 0, + backtrace: NO_EXPANSION, + module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }), + in_block: false, + }, } } @@ -609,23 +553,22 @@ impl<'a> ExtCtxt<'a> { -> parser::Parser<'a> { parse::tts_to_parser(self.parse_sess, tts.to_vec(), self.cfg()) } - pub fn codemap(&self) -> &'a CodeMap { self.parse_sess.codemap() } pub fn parse_sess(&self) -> &'a parse::ParseSess { self.parse_sess } pub fn cfg(&self) -> ast::CrateConfig { self.cfg.clone() } pub fn call_site(&self) -> Span { - self.codemap().with_expn_info(self.backtrace, |ei| match ei { + self.codemap().with_expn_info(self.backtrace(), |ei| match ei { Some(expn_info) => expn_info.call_site, None => self.bug("missing top span") }) } - pub fn backtrace(&self) -> ExpnId { self.backtrace } + pub fn backtrace(&self) -> ExpnId { self.current_expansion.backtrace } /// Returns span for the macro which originally caused the current expansion to happen. /// /// Stops backtracing at include! boundary. pub fn expansion_cause(&self) -> Span { - let mut expn_id = self.backtrace; + let mut expn_id = self.backtrace(); let mut last_macro = None; loop { if self.codemap().with_expn_info(expn_id, |info| { @@ -646,15 +589,15 @@ impl<'a> ExtCtxt<'a> { } pub fn bt_push(&mut self, ei: ExpnInfo) { - if self.recursion_count > self.ecfg.recursion_limit { + if self.current_expansion.depth > self.ecfg.recursion_limit { self.span_fatal(ei.call_site, &format!("recursion limit reached while expanding the macro `{}`", ei.callee.name())); } let mut call_site = ei.call_site; - call_site.expn_id = self.backtrace; - self.backtrace = self.codemap().record_expansion(ExpnInfo { + call_site.expn_id = self.backtrace(); + self.current_expansion.backtrace = self.codemap().record_expansion(ExpnInfo { call_site: call_site, callee: ei.callee }); @@ -667,14 +610,11 @@ impl<'a> ExtCtxt<'a> { } if def.use_locally { let ext = macro_rules::compile(self, &def); - self.syntax_env.insert(def.ident.name, ext); + self.resolver.add_macro(self.current_expansion.mark, def.ident, Rc::new(ext)); } } - pub fn insert_custom_derive(&mut self, - name: &str, - ext: Box, - sp: Span) { + pub fn insert_custom_derive(&mut self, name: &str, ext: Box, sp: Span) { if !self.ecfg.enable_rustc_macro() { feature_gate::emit_feature_err(&self.parse_sess.span_diagnostic, "rustc_macro", @@ -685,8 +625,7 @@ impl<'a> ExtCtxt<'a> { } let name = token::intern_and_get_ident(name); if self.derive_modes.insert(name.clone(), ext).is_some() { - self.span_err(sp, &format!("cannot shadow existing derive mode `{}`", - name)); + self.span_err(sp, &format!("cannot shadow existing derive mode `{}`", name)); } } @@ -765,20 +704,6 @@ impl<'a> ExtCtxt<'a> { token::intern(st) } - pub fn suggest_macro_name(&mut self, - name: &str, - err: &mut DiagnosticBuilder<'a>) { - let names = &self.syntax_env.names; - if let Some(suggestion) = find_best_match_for_name(names.iter(), name, None) { - if suggestion != name { - err.help(&format!("did you mean `{}!`?", suggestion)); - } else { - err.help(&format!("have you added the `#[macro_use]` on the \ - module/import?")); - } - } - } - pub fn initialize(&mut self, user_exts: Vec, krate: &ast::Crate) { if std_inject::no_core(&krate) { self.crate_root = None; @@ -789,16 +714,16 @@ impl<'a> ExtCtxt<'a> { } for (name, extension) in user_exts { - self.syntax_env.insert(name, extension); + let ident = ast::Ident::with_empty_ctxt(name); + self.resolver.add_macro(Mark::root(), ident, Rc::new(extension)); } - self.syntax_env.current_module = Module(0); - let mut paths = ModulePaths { + let mut module = ModuleData { mod_path: vec![token::str_to_ident(&self.ecfg.crate_name)], directory: PathBuf::from(self.parse_sess.codemap().span_to_filename(krate.span)), }; - paths.directory.pop(); - self.syntax_env.module_data[0].paths = Rc::new(paths); + module.directory.pop(); + self.current_expansion.module = Rc::new(module); } } @@ -809,7 +734,7 @@ pub fn expr_to_spanned_string(cx: &mut ExtCtxt, expr: P, err_msg: &st -> Option> { // Update `expr.span`'s expn_id now in case expr is an `include!` macro invocation. let expr = expr.map(|mut expr| { - expr.span.expn_id = cx.backtrace; + expr.span.expn_id = cx.backtrace(); expr }); @@ -884,104 +809,3 @@ pub fn get_exprs_from_tts(cx: &mut ExtCtxt, } Some(es) } - -/// In order to have some notion of scoping for macros, -/// we want to implement the notion of a transformation -/// environment. -/// -/// This environment maps Names to SyntaxExtensions. -pub struct SyntaxEnv { - module_data: Vec, - pub current_module: Module, - - /// All bang-style macro/extension names - /// encountered so far; to be used for diagnostics in resolve - pub names: HashSet, -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub struct Module(u32); - -struct ModuleData { - parent: Module, - paths: Rc, - macros: HashMap>, - macros_escape: bool, - in_block: bool, -} - -#[derive(Clone)] -pub struct ModulePaths { - pub mod_path: Vec, - pub directory: PathBuf, -} - -impl SyntaxEnv { - fn new() -> SyntaxEnv { - let mut env = SyntaxEnv { - current_module: Module(0), - module_data: Vec::new(), - names: HashSet::new(), - }; - let paths = Rc::new(ModulePaths { mod_path: Vec::new(), directory: PathBuf::new() }); - env.add_module(false, false, paths); - env - } - - fn data(&self, module: Module) -> &ModuleData { - &self.module_data[module.0 as usize] - } - - pub fn paths(&self) -> Rc { - self.data(self.current_module).paths.clone() - } - - pub fn in_block(&self) -> bool { - self.data(self.current_module).in_block - } - - pub fn add_module(&mut self, macros_escape: bool, in_block: bool, paths: Rc) - -> Module { - let data = ModuleData { - parent: self.current_module, - paths: paths, - macros: HashMap::new(), - macros_escape: macros_escape, - in_block: in_block, - }; - - self.module_data.push(data); - Module(self.module_data.len() as u32 - 1) - } - - pub fn find(&self, name: Name) -> Option> { - let mut module = self.current_module; - let mut module_data; - loop { - module_data = self.data(module); - if let Some(ext) = module_data.macros.get(&name) { - return Some(ext.clone()); - } - if module == module_data.parent { - return None; - } - module = module_data.parent; - } - } - - pub fn insert(&mut self, name: Name, ext: SyntaxExtension) { - if let NormalTT(..) = ext { - self.names.insert(name); - } - - let mut module = self.current_module; - while self.data(module).macros_escape { - module = self.data(module).parent; - } - self.module_data[module.0 as usize].macros.insert(name, Rc::new(ext)); - } - - pub fn is_crate_root(&mut self) -> bool { - self.current_module == Module(0) - } -} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d8365391153..beb1687dfdc 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -25,6 +25,7 @@ use parse::token::{intern, keywords}; use ptr::P; use tokenstream::TokenTree; use util::small_vector::SmallVector; +use visit::Visitor; use std::mem; use std::path::PathBuf; @@ -32,7 +33,8 @@ use std::rc::Rc; macro_rules! expansions { ($($kind:ident: $ty:ty [$($vec:ident, $ty_elt:ty)*], $kind_name:expr, .$make:ident, - $(.$fold:ident)* $(lift .$fold_elt:ident)*;)*) => { + $(.$fold:ident)* $(lift .$fold_elt:ident)*, + $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { #[derive(Copy, Clone)] pub enum ExpansionKind { OptExpr, $( $kind, )* } pub enum Expansion { OptExpr(Option>), $( $kind($ty), )* } @@ -77,6 +79,17 @@ macro_rules! expansions { }, )*)* } } + + pub fn visit_with(&self, visitor: &mut V) { + match *self { + Expansion::OptExpr(Some(ref expr)) => visitor.visit_expr(expr), + Expansion::OptExpr(None) => {} + $($( Expansion::$kind(ref ast) => visitor.$visit(ast), )*)* + $($( Expansion::$kind(ref ast) => for ast in ast.as_slice() { + visitor.$visit_elt(ast); + }, )*)* + } + } } impl<'a, 'b> Folder for MacroExpander<'a, 'b> { @@ -94,17 +107,17 @@ macro_rules! expansions { } expansions! { - Expr: P [], "expression", .make_expr, .fold_expr; - Pat: P [], "pattern", .make_pat, .fold_pat; - Ty: P [], "type", .make_ty, .fold_ty; + Expr: P [], "expression", .make_expr, .fold_expr, .visit_expr; + Pat: P [], "pattern", .make_pat, .fold_pat, .visit_pat; + Ty: P [], "type", .make_ty, .fold_ty, .visit_ty; Stmts: SmallVector [SmallVector, ast::Stmt], - "statement", .make_stmts, lift .fold_stmt; + "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt; Items: SmallVector> [SmallVector, P], - "item", .make_items, lift .fold_item; + "item", .make_items, lift .fold_item, lift .visit_item; TraitItems: SmallVector [SmallVector, ast::TraitItem], - "trait item", .make_trait_items, lift .fold_trait_item; + "trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item; ImplItems: SmallVector [SmallVector, ast::ImplItem], - "impl item", .make_impl_items, lift .fold_impl_item; + "impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item; } impl ExpansionKind { @@ -127,15 +140,12 @@ impl ExpansionKind { } pub struct Invocation { - kind: InvocationKind, + pub kind: InvocationKind, expansion_kind: ExpansionKind, - mark: Mark, - module: Module, - backtrace: ExpnId, - depth: usize, + expansion_data: ExpansionData, } -enum InvocationKind { +pub enum InvocationKind { Bang { attrs: Vec, mac: ast::Mac, @@ -148,6 +158,19 @@ enum InvocationKind { }, } +impl Invocation { + fn span(&self) -> Span { + match self.kind { + InvocationKind::Bang { span, .. } => span, + InvocationKind::Attr { ref attr, .. } => attr.span, + } + } + + pub fn mark(&self) -> Mark { + self.expansion_data.mark + } +} + pub struct MacroExpander<'a, 'b:'a> { pub cx: &'a mut ExtCtxt<'b>, pub single_step: bool, @@ -170,7 +193,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let items = Expansion::Items(SmallVector::many(krate.module.items)); krate.module.items = self.expand(items).make_items().into(); - krate.exported_macros = self.cx.exported_macros.clone(); + krate.exported_macros = mem::replace(&mut self.cx.exported_macros, Vec::new()); if self.cx.parse_sess.span_diagnostic.err_count() > err_count { self.cx.parse_sess.span_diagnostic.abort_if_errors(); @@ -181,21 +204,23 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // Fully expand all the invocations in `expansion`. fn expand(&mut self, expansion: Expansion) -> Expansion { - self.cx.recursion_count = 0; + let orig_expansion_data = self.cx.current_expansion.clone(); + self.cx.current_expansion.depth = 0; + let (expansion, mut invocations) = self.collect_invocations(expansion); invocations.reverse(); let mut expansions = vec![vec![(0, expansion)]]; while let Some(invoc) = invocations.pop() { - let Invocation { mark, module, depth, backtrace, .. } = invoc; - self.cx.syntax_env.current_module = module; - self.cx.recursion_count = depth; - self.cx.backtrace = backtrace; + let ExpansionData { depth, mark, .. } = invoc.expansion_data; + self.cx.current_expansion = invoc.expansion_data.clone(); - let expansion = self.expand_invoc(invoc); + let expansion = match self.cx.resolver.resolve_invoc(&invoc) { + Some(ext) => self.expand_invoc(invoc, ext), + None => invoc.expansion_kind.dummy(invoc.span()), + }; - self.cx.syntax_env.current_module = module; - self.cx.recursion_count = depth + 1; + self.cx.current_expansion.depth = depth + 1; let (expansion, new_invocations) = self.collect_invocations(expansion); if expansions.len() == depth { @@ -207,6 +232,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } + self.cx.current_expansion = orig_expansion_data; + let mut placeholder_expander = PlaceholderExpander::new(); while let Some(expansions) = expansions.pop() { for (mark, expansion) in expansions.into_iter().rev() { @@ -233,30 +260,27 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }; (expansion.fold_with(&mut collector), collector.invocations) }; - self.cx.cfg = crate_config; + + let mark = self.cx.current_expansion.mark; + self.cx.resolver.visit_expansion(mark, &result.0); result } - fn expand_invoc(&mut self, invoc: Invocation) -> Expansion { + fn expand_invoc(&mut self, invoc: Invocation, ext: Rc) -> Expansion { match invoc.kind { - InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc), - InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc), + InvocationKind::Bang { .. } => self.expand_bang_invoc(invoc, ext), + InvocationKind::Attr { .. } => self.expand_attr_invoc(invoc, ext), } } - fn expand_attr_invoc(&mut self, invoc: Invocation) -> Expansion { + fn expand_attr_invoc(&mut self, invoc: Invocation, ext: Rc) -> Expansion { let Invocation { expansion_kind: kind, .. } = invoc; let (attr, item) = match invoc.kind { InvocationKind::Attr { attr, item } => (attr, item), _ => unreachable!(), }; - let extension = match self.cx.syntax_env.find(intern(&attr.name())) { - Some(extension) => extension, - None => unreachable!(), - }; - attr::mark_used(&attr); self.cx.bt_push(ExpnInfo { call_site: attr.span, @@ -267,7 +291,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } }); - match *extension { + match *ext { MultiModifier(ref mac) => { let item = mac.expand(self.cx, attr.span, &attr.node.value, item); kind.expect_from_annotatables(item) @@ -284,8 +308,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } /// Expand a macro invocation. Returns the result of expansion. - fn expand_bang_invoc(&mut self, invoc: Invocation) -> Expansion { - let Invocation { mark, expansion_kind: kind, .. } = invoc; + fn expand_bang_invoc(&mut self, invoc: Invocation, ext: Rc) -> Expansion { + let (mark, kind) = (invoc.mark(), invoc.expansion_kind); let (attrs, mac, ident, span) = match invoc.kind { InvocationKind::Bang { attrs, mac, ident, span } => (attrs, mac, ident, span), _ => unreachable!(), @@ -306,19 +330,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } let extname = path.segments[0].identifier.name; - let extension = if let Some(extension) = self.cx.syntax_env.find(extname) { - extension - } else { - let mut err = - self.cx.struct_span_err(path.span, &format!("macro undefined: '{}!'", &extname)); - self.cx.suggest_macro_name(&extname.as_str(), &mut err); - err.emit(); - return kind.dummy(span); - }; - let ident = ident.unwrap_or(keywords::Invalid.ident()); let marked_tts = mark_tts(&tts, mark); - let opt_expanded = match *extension { + let opt_expanded = match *ext { NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { if ident.name != keywords::Invalid.name() { let msg = @@ -442,10 +456,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.invocations.push(Invocation { kind: kind, expansion_kind: expansion_kind, - mark: mark, - module: self.cx.syntax_env.current_module, - backtrace: self.cx.backtrace, - depth: self.cx.recursion_count, + expansion_data: ExpansionData { mark: mark, ..self.cx.current_expansion.clone() }, }); placeholder(expansion_kind, mark.as_u32()) } @@ -462,50 +473,15 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } // If `item` is an attr invocation, remove and return the macro attribute. - fn classify_item(&self, mut item: T) -> (T, Option) { + fn classify_item(&mut self, mut item: T) -> (T, Option) { let mut attr = None; item = item.map_attrs(|mut attrs| { - for i in 0..attrs.len() { - if let Some(extension) = self.cx.syntax_env.find(intern(&attrs[i].name())) { - match *extension { - MultiModifier(..) | MultiDecorator(..) => { - attr = Some(attrs.remove(i)); - break; - } - _ => {} - } - } - } + attr = self.cx.resolver.find_attr_invoc(&mut attrs); attrs }); (item, attr) } - // does this attribute list contain "macro_use" ? - fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool { - for attr in attrs { - let mut is_use = attr.check_name("macro_use"); - if attr.check_name("macro_escape") { - let msg = "macro_escape is a deprecated synonym for macro_use"; - let mut err = self.cx.struct_span_warn(attr.span, msg); - is_use = true; - if let ast::AttrStyle::Inner = attr.node.style { - err.help("consider an outer attribute, #[macro_use] mod ...").emit(); - } else { - err.emit(); - } - }; - - if is_use { - if !attr.is_word() { - self.cx.span_err(attr.span, "arguments to macro_use are not allowed here"); - } - return true; - } - } - false - } - fn configure(&mut self, node: T) -> Option { self.cfg.configure(node) } @@ -574,11 +550,9 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { } fn fold_block(&mut self, block: P) -> P { - let paths = self.cx.syntax_env.paths(); - let module = self.cx.syntax_env.add_module(false, true, paths); - let orig_module = mem::replace(&mut self.cx.syntax_env.current_module, module); + let orig_in_block = mem::replace(&mut self.cx.current_expansion.in_block, true); let result = noop_fold_block(block, self); - self.cx.syntax_env.current_module = orig_module; + self.cx.current_expansion.in_block = orig_in_block; result } @@ -613,8 +587,8 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { }) } ast::ItemKind::Mod(ast::Mod { inner, .. }) => { - let mut paths = (*self.cx.syntax_env.paths()).clone(); - paths.mod_path.push(item.ident); + let mut module = (*self.cx.current_expansion.module).clone(); + module.mod_path.push(item.ident); // Detect if this is an inline module (`mod m { ... }` as opposed to `mod m;`). // In the non-inline case, `inner` is never the dummy span (c.f. `parse_item_mod`). @@ -622,28 +596,26 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { let inline_module = item.span.contains(inner) || inner == syntax_pos::DUMMY_SP; if inline_module { - paths.directory.push(&*{ + module.directory.push(&*{ ::attr::first_attr_value_str_by_name(&item.attrs, "path") .unwrap_or(item.ident.name.as_str()) }); } else { - paths.directory = + module.directory = PathBuf::from(self.cx.parse_sess.codemap().span_to_filename(inner)); - paths.directory.pop(); + module.directory.pop(); } - let macro_use = self.contains_macro_use(&item.attrs); - let in_block = self.cx.syntax_env.in_block(); - let module = self.cx.syntax_env.add_module(macro_use, in_block, Rc::new(paths)); - let module = mem::replace(&mut self.cx.syntax_env.current_module, module); + let orig_module = + mem::replace(&mut self.cx.current_expansion.module, Rc::new(module)); let result = noop_fold_item(item, self); - self.cx.syntax_env.current_module = module; - result - }, + self.cx.current_expansion.module = orig_module; + return result; + } ast::ItemKind::ExternCrate(..) => { // We need to error on `#[macro_use] extern crate` when it isn't at the // crate root, because `$crate` won't work properly. - let is_crate_root = self.cx.syntax_env.is_crate_root(); + let is_crate_root = self.cx.current_expansion.module.mod_path.len() == 1; for def in self.cx.resolver.load_crate(&*item, is_crate_root) { match def { LoadedMacro::Def(def) => self.cx.insert_macro(def), diff --git a/src/libsyntax/ext/hygiene.rs b/src/libsyntax/ext/hygiene.rs index 27e8eab62e1..34126fac4ac 100644 --- a/src/libsyntax/ext/hygiene.rs +++ b/src/libsyntax/ext/hygiene.rs @@ -29,7 +29,7 @@ pub struct SyntaxContextData { pub prev_ctxt: SyntaxContext, } -/// A mark represents a unique id associated with a macro expansion. +/// A mark is a unique id associated with a macro expansion. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)] pub struct Mark(u32); @@ -41,6 +41,11 @@ impl Mark { }) } + /// The mark of the theoretical expansion that generates freshly parsed, unexpanded AST. + pub fn root() -> Self { + Mark(0) + } + pub fn as_u32(&self) -> u32 { self.0 } @@ -56,8 +61,8 @@ impl HygieneData { fn new() -> Self { HygieneData { syntax_contexts: vec![SyntaxContextData { - outer_mark: Mark(0), // the null mark - prev_ctxt: SyntaxContext(0), // the empty context + outer_mark: Mark::root(), + prev_ctxt: SyntaxContext::empty(), }], markings: HashMap::new(), next_mark: Mark(1), diff --git a/src/libsyntax/ext/source_util.rs b/src/libsyntax/ext/source_util.rs index 105b2261117..e75e41d0c2d 100644 --- a/src/libsyntax/ext/source_util.rs +++ b/src/libsyntax/ext/source_util.rs @@ -74,8 +74,8 @@ pub fn expand_stringify(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTre pub fn expand_mod(cx: &mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree]) -> Box { base::check_zero_tts(cx, sp, tts, "module_path!"); - let paths = cx.syntax_env.paths(); - let string = paths.mod_path.iter().map(|x| x.to_string()).collect::>().join("::"); + let mod_path = &cx.current_expansion.module.mod_path; + let string = mod_path.iter().map(|x| x.to_string()).collect::>().join("::"); base::MacEager::expr(cx.expr_str( sp, diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index ed80ec9cbc4..51ef45b97be 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -211,8 +211,8 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, imported_from, rhs); let mut p = Parser::new(cx.parse_sess(), cx.cfg(), Box::new(trncbr)); - p.directory = cx.syntax_env.paths().directory.clone(); - p.restrictions = match cx.syntax_env.in_block() { + p.directory = cx.current_expansion.module.directory.clone(); + p.restrictions = match cx.current_expansion.in_block { true => Restrictions::NO_NONINLINE_MOD, false => Restrictions::empty(), }; diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index fcbce363890..6162beb80ec 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -11,8 +11,7 @@ //! The compiler code necessary to implement the `#[derive]` extensions. use syntax::ast::{self, MetaItem}; -use syntax::ext::base::{Annotatable, ExtCtxt, SyntaxEnv}; -use syntax::ext::base::MultiModifier; +use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::build::AstBuilder; use syntax::feature_gate; use syntax::codemap; @@ -89,7 +88,7 @@ fn allow_unstable(cx: &mut ExtCtxt, span: Span, attr_name: &str) -> Span { } } -fn expand_derive(cx: &mut ExtCtxt, +pub fn expand_derive(cx: &mut ExtCtxt, span: Span, mitem: &MetaItem, annotatable: Annotatable) @@ -243,10 +242,6 @@ fn expand_derive(cx: &mut ExtCtxt, macro_rules! derive_traits { ($( $name:expr => $func:path, )+) => { - pub fn register_all(env: &mut SyntaxEnv) { - env.insert(intern("derive"), MultiModifier(Box::new(expand_derive))); - } - pub fn is_builtin_trait(name: &str) -> bool { match name { $( $name )|+ => true, diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 2065d92fd6e..3a6212e5445 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -34,11 +34,6 @@ extern crate syntax_pos; extern crate rustc_macro; extern crate rustc_errors as errors; -use syntax::ext::base::{MacroExpanderFn, NormalTT}; -use syntax::ext::base::{SyntaxEnv, SyntaxExtension}; -use syntax::parse::token::intern; - - mod asm; mod cfg; mod concat; @@ -53,28 +48,67 @@ pub mod rustc_macro_registrar; // for custom_derive pub mod deriving; -pub fn register_builtins(env: &mut SyntaxEnv) { - // utility function to simplify creating NormalTT syntax extensions - fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension { - NormalTT(Box::new(f), None, false) +use std::rc::Rc; +use syntax::ast; +use syntax::ext::base::{MacroExpanderFn, MacroRulesTT, NormalTT, MultiModifier}; +use syntax::ext::hygiene::Mark; +use syntax::parse::token::intern; + +pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, enable_quotes: bool) { + let mut register = |name, ext| { + resolver.add_macro(Mark::root(), ast::Ident::with_empty_ctxt(intern(name)), Rc::new(ext)); + }; + + register("macro_rules", MacroRulesTT); + + macro_rules! register { + ($( $name:ident: $f:expr, )*) => { $( + register(stringify!($name), NormalTT(Box::new($f as MacroExpanderFn), None, false)); + )* } } - env.insert(intern("asm"), builtin_normal_expander(asm::expand_asm)); - env.insert(intern("cfg"), builtin_normal_expander(cfg::expand_cfg)); - env.insert(intern("concat"), - builtin_normal_expander(concat::expand_syntax_ext)); - env.insert(intern("concat_idents"), - builtin_normal_expander(concat_idents::expand_syntax_ext)); - env.insert(intern("env"), builtin_normal_expander(env::expand_env)); - env.insert(intern("option_env"), - builtin_normal_expander(env::expand_option_env)); - env.insert(intern("format_args"), - // format_args uses `unstable` things internally. - NormalTT(Box::new(format::expand_format_args), None, true)); - env.insert(intern("log_syntax"), - builtin_normal_expander(log_syntax::expand_syntax_ext)); - env.insert(intern("trace_macros"), - builtin_normal_expander(trace_macros::expand_trace_macros)); - - deriving::register_all(env); + if enable_quotes { + use syntax::ext::quote::*; + register! { + quote_tokens: expand_quote_tokens, + quote_expr: expand_quote_expr, + quote_ty: expand_quote_ty, + quote_item: expand_quote_item, + quote_pat: expand_quote_pat, + quote_arm: expand_quote_arm, + quote_stmt: expand_quote_stmt, + quote_matcher: expand_quote_matcher, + quote_attr: expand_quote_attr, + quote_arg: expand_quote_arg, + quote_block: expand_quote_block, + quote_meta_item: expand_quote_meta_item, + quote_path: expand_quote_path, + } + } + + use syntax::ext::source_util::*; + register! { + line: expand_line, + column: expand_column, + file: expand_file, + stringify: expand_stringify, + include: expand_include, + include_str: expand_include_str, + include_bytes: expand_include_bytes, + module_path: expand_mod, + + asm: asm::expand_asm, + cfg: cfg::expand_cfg, + concat: concat::expand_syntax_ext, + concat_idents: concat_idents::expand_syntax_ext, + env: env::expand_env, + option_env: env::expand_option_env, + log_syntax: log_syntax::expand_syntax_ext, + trace_macros: trace_macros::expand_trace_macros, + } + + // format_args uses `unstable` things internally. + register("format_args", NormalTT(Box::new(format::expand_format_args), None, true)); + + register("derive", MultiModifier(Box::new(deriving::expand_derive))); } -- cgit 1.4.1-3-g733a5 From 78c00398780db6f59ebf43e765fa9368dad436d2 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 6 Sep 2016 07:52:09 +0000 Subject: Expand generated test harnesses and macro registries. --- src/librustc_driver/driver.rs | 2 ++ src/libsyntax/test.rs | 30 +++++++++++++++--------------- src/libsyntax_ext/rustc_macro_registrar.rs | 23 ++++++++++------------- 3 files changed, 27 insertions(+), 28 deletions(-) (limited to 'src/libsyntax_ext') diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 007222b2edf..36e9fccdf5f 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -690,6 +690,7 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, krate = time(time_passes, "maybe building test harness", || { syntax::test::modify_for_testing(&sess.parse_sess, + &mut resolver, sess.opts.test, krate, sess.diagnostic()) @@ -700,6 +701,7 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, let is_rustc_macro_crate = crate_types.contains(&config::CrateTypeRustcMacro); let num_crate_types = crate_types.len(); syntax_ext::rustc_macro_registrar::modify(&sess.parse_sess, + &mut resolver, krate, is_rustc_macro_crate, num_crate_types, diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index dde8a8d271f..46c9a4606cc 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -28,7 +28,7 @@ use errors; use errors::snippet::{SnippetData}; use config; use entry::{self, EntryPointType}; -use ext::base::{ExtCtxt, DummyResolver}; +use ext::base::{ExtCtxt, Resolver}; use ext::build::AstBuilder; use ext::expand::ExpansionConfig; use fold::Folder; @@ -70,6 +70,7 @@ struct TestCtxt<'a> { // Traverse the crate, collecting all the test functions, eliding any // existing main functions, and synthesizing a main test harness pub fn modify_for_testing(sess: &ParseSess, + resolver: &mut Resolver, should_test: bool, krate: ast::Crate, span_diagnostic: &errors::Handler) -> ast::Crate { @@ -82,7 +83,7 @@ pub fn modify_for_testing(sess: &ParseSess, "reexport_test_harness_main"); if should_test { - generate_test_harness(sess, reexport_test_harness_main, krate, span_diagnostic) + generate_test_harness(sess, resolver, reexport_test_harness_main, krate, span_diagnostic) } else { krate } @@ -248,27 +249,28 @@ fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec, }).chain(tested_submods.into_iter().map(|(r, sym)| { let path = cx.ext_cx.path(DUMMY_SP, vec![super_, r, sym]); cx.ext_cx.item_use_simple_(DUMMY_SP, ast::Visibility::Public, r, path) - })); + })).collect(); let reexport_mod = ast::Mod { inner: DUMMY_SP, - items: items.collect(), + items: items, }; let sym = token::gensym_ident("__test_reexports"); - let it = P(ast::Item { + let it = cx.ext_cx.expander().fold_item(P(ast::Item { ident: sym.clone(), attrs: Vec::new(), id: ast::DUMMY_NODE_ID, node: ast::ItemKind::Mod(reexport_mod), vis: ast::Visibility::Public, span: DUMMY_SP, - }); + })).pop().unwrap(); (it, sym) } fn generate_test_harness(sess: &ParseSess, + resolver: &mut Resolver, reexport_test_harness_main: Option, krate: ast::Crate, sd: &errors::Handler) -> ast::Crate { @@ -276,13 +278,10 @@ fn generate_test_harness(sess: &ParseSess, let mut cleaner = EntryPointCleaner { depth: 0 }; let krate = cleaner.fold_crate(krate); - let mut resolver = DummyResolver; let mut cx: TestCtxt = TestCtxt { sess: sess, span_diagnostic: sd, - ext_cx: ExtCtxt::new(sess, vec![], - ExpansionConfig::default("test".to_string()), - &mut resolver), + ext_cx: ExtCtxt::new(sess, vec![], ExpansionConfig::default("test".to_string()), resolver), path: Vec::new(), testfns: Vec::new(), reexport_test_harness_main: reexport_test_harness_main, @@ -511,16 +510,17 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P, Option>) { items: vec![import, mainfn, tests], }; let item_ = ast::ItemKind::Mod(testmod); - let mod_ident = token::gensym_ident("__test"); - let item = P(ast::Item { + + let mut expander = cx.ext_cx.expander(); + let item = expander.fold_item(P(ast::Item { id: ast::DUMMY_NODE_ID, ident: mod_ident, attrs: vec![], node: item_, vis: ast::Visibility::Public, span: DUMMY_SP, - }); + })).pop().unwrap(); let reexport = cx.reexport_test_harness_main.as_ref().map(|s| { // building `use = __test::main` let reexport_ident = token::str_to_ident(&s); @@ -529,14 +529,14 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P, Option>) { nospan(ast::ViewPathSimple(reexport_ident, path_node(vec![mod_ident, token::str_to_ident("main")]))); - P(ast::Item { + expander.fold_item(P(ast::Item { id: ast::DUMMY_NODE_ID, ident: keywords::Invalid.ident(), attrs: vec![], node: ast::ItemKind::Use(P(use_path)), vis: ast::Visibility::Inherited, span: DUMMY_SP - }) + })).pop().unwrap() }); debug!("Synthetic test module:\n{}\n", pprust::item_to_string(&item)); diff --git a/src/libsyntax_ext/rustc_macro_registrar.rs b/src/libsyntax_ext/rustc_macro_registrar.rs index 78fed9d33dd..c07e7917939 100644 --- a/src/libsyntax_ext/rustc_macro_registrar.rs +++ b/src/libsyntax_ext/rustc_macro_registrar.rs @@ -13,12 +13,13 @@ use std::mem; use errors; use syntax::ast::{self, Ident, NodeId}; use syntax::codemap::{ExpnInfo, NameAndSpan, MacroAttribute}; -use syntax::ext::base::{ExtCtxt, DummyResolver}; +use syntax::ext::base::ExtCtxt; use syntax::ext::build::AstBuilder; use syntax::ext::expand::ExpansionConfig; use syntax::parse::ParseSess; use syntax::parse::token::{self, InternedString}; use syntax::feature_gate::Features; +use syntax::fold::Folder; use syntax::ptr::P; use syntax_pos::{Span, DUMMY_SP}; use syntax::visit::{self, Visitor}; @@ -39,16 +40,14 @@ struct CollectCustomDerives<'a> { } pub fn modify(sess: &ParseSess, + resolver: &mut ::syntax::ext::base::Resolver, mut krate: ast::Crate, is_rustc_macro_crate: bool, num_crate_types: usize, handler: &errors::Handler, features: &Features) -> ast::Crate { - let mut loader = DummyResolver; - let mut cx = ExtCtxt::new(sess, - Vec::new(), - ExpansionConfig::default("rustc_macro".to_string()), - &mut loader); + let ecfg = ExpansionConfig::default("rustc_macro".to_string()); + let mut cx = ExtCtxt::new(sess, Vec::new(), ecfg, resolver); let mut collect = CollectCustomDerives { derives: Vec::new(), @@ -268,13 +267,11 @@ fn mk_registrar(cx: &mut ExtCtxt, i.vis = ast::Visibility::Public; i }); - let module = cx.item_mod(span, - span, - ast::Ident::with_empty_ctxt(token::gensym("registrar")), - Vec::new(), - vec![krate, func]); - module.map(|mut i| { + let ident = ast::Ident::with_empty_ctxt(token::gensym("registrar")); + let module = cx.item_mod(span, span, ident, Vec::new(), vec![krate, func]).map(|mut i| { i.vis = ast::Visibility::Public; i - }) + }); + + cx.expander().fold_item(module).pop().unwrap() } -- cgit 1.4.1-3-g733a5 From b54e1e399741579612f13e2df98a25ea9447989d Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 6 Sep 2016 05:42:45 +0000 Subject: Differentiate between monotonic and non-monotonic expansion and only assign node ids during monotonic expansion. --- src/libsyntax/ext/base.rs | 8 +++++++- src/libsyntax/ext/expand.rs | 24 +++++++++++++++--------- src/libsyntax/ext/placeholders.rs | 10 +++++++--- src/libsyntax/test.rs | 4 ++-- src/libsyntax_ext/rustc_macro_registrar.rs | 2 +- 5 files changed, 32 insertions(+), 16 deletions(-) (limited to 'src/libsyntax_ext') diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 1e8f8ef9ddd..fb4816d3847 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -549,7 +549,13 @@ impl<'a> ExtCtxt<'a> { /// Returns a `Folder` for deeply expanding all macros in an AST node. pub fn expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { - expand::MacroExpander::new(self, false, false) + expand::MacroExpander::new(self, false) + } + + /// Returns a `Folder` that deeply expands all macros and assigns all node ids in an AST node. + /// Once node ids are assigned, the node may not be expanded, removed, or otherwise modified. + pub fn monotonic_expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { + expand::MacroExpander::new(self, true) } pub fn new_parser_from_tts(&self, tts: &[tokenstream::TokenTree]) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 0eb9d4bc0c2..62e299684b7 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -175,16 +175,16 @@ pub struct MacroExpander<'a, 'b:'a> { pub cx: &'a mut ExtCtxt<'b>, pub single_step: bool, pub keep_macs: bool, + monotonic: bool, // c.f. `cx.monotonic_expander()` } impl<'a, 'b> MacroExpander<'a, 'b> { - pub fn new(cx: &'a mut ExtCtxt<'b>, - single_step: bool, - keep_macs: bool) -> MacroExpander<'a, 'b> { + pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self { MacroExpander { cx: cx, - single_step: single_step, - keep_macs: keep_macs + monotonic: monotonic, + single_step: false, + keep_macs: false, } } @@ -245,7 +245,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.current_expansion = orig_expansion_data; - let mut placeholder_expander = PlaceholderExpander::new(self.cx); + let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic); while let Some(expansions) = expansions.pop() { for (mark, expansion) in expansions.into_iter().rev() { let expansion = expansion.fold_with(&mut placeholder_expander); @@ -268,6 +268,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { }, cx: self.cx, invocations: Vec::new(), + monotonic: self.monotonic, }; (expansion.fold_with(&mut collector), collector.invocations) }; @@ -450,6 +451,7 @@ struct InvocationCollector<'a, 'b: 'a> { cx: &'a mut ExtCtxt<'b>, cfg: StripUnconfigured<'a>, invocations: Vec, + monotonic: bool, } macro_rules! fully_configure { @@ -709,8 +711,12 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> { } fn new_id(&mut self, id: ast::NodeId) -> ast::NodeId { - assert_eq!(id, ast::DUMMY_NODE_ID); - self.cx.resolver.next_node_id() + if self.monotonic { + assert_eq!(id, ast::DUMMY_NODE_ID); + self.cx.resolver.next_node_id() + } else { + id + } } } @@ -763,7 +769,7 @@ pub fn expand_crate(cx: &mut ExtCtxt, user_exts: Vec, c: Crate) -> Crate { cx.initialize(user_exts, &c); - cx.expander().expand_crate(c) + cx.monotonic_expander().expand_crate(c) } // Expands crate using supplied MacroExpander - allows for diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs index 7635705daa0..47f366a8876 100644 --- a/src/libsyntax/ext/placeholders.rs +++ b/src/libsyntax/ext/placeholders.rs @@ -75,13 +75,15 @@ pub fn macro_scope_placeholder() -> Expansion { pub struct PlaceholderExpander<'a, 'b: 'a> { expansions: HashMap, cx: &'a mut ExtCtxt<'b>, + monotonic: bool, } impl<'a, 'b> PlaceholderExpander<'a, 'b> { - pub fn new(cx: &'a mut ExtCtxt<'b>) -> Self { + pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self { PlaceholderExpander { cx: cx, expansions: HashMap::new(), + monotonic: monotonic, } } @@ -182,13 +184,15 @@ impl<'a, 'b> Folder for PlaceholderExpander<'a, 'b> { // which shares a HIR node with the expression itself. ast::StmtKind::Expr(ref expr) if remaining_stmts == 0 => stmt.id = expr.id, - _ => { + _ if self.monotonic => { assert_eq!(stmt.id, ast::DUMMY_NODE_ID); stmt.id = self.cx.resolver.next_node_id(); } + + _ => {} } - if !macros.is_empty() { + if self.monotonic && !macros.is_empty() { let macros = mem::replace(&mut macros, Vec::new()); self.cx.resolver.add_expansions_at_stmt(stmt.id, macros); } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 46c9a4606cc..6327e8f71bc 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -257,7 +257,7 @@ fn mk_reexport_mod(cx: &mut TestCtxt, tests: Vec, }; let sym = token::gensym_ident("__test_reexports"); - let it = cx.ext_cx.expander().fold_item(P(ast::Item { + let it = cx.ext_cx.monotonic_expander().fold_item(P(ast::Item { ident: sym.clone(), attrs: Vec::new(), id: ast::DUMMY_NODE_ID, @@ -512,7 +512,7 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P, Option>) { let item_ = ast::ItemKind::Mod(testmod); let mod_ident = token::gensym_ident("__test"); - let mut expander = cx.ext_cx.expander(); + let mut expander = cx.ext_cx.monotonic_expander(); let item = expander.fold_item(P(ast::Item { id: ast::DUMMY_NODE_ID, ident: mod_ident, diff --git a/src/libsyntax_ext/rustc_macro_registrar.rs b/src/libsyntax_ext/rustc_macro_registrar.rs index c07e7917939..ce3e53cdf97 100644 --- a/src/libsyntax_ext/rustc_macro_registrar.rs +++ b/src/libsyntax_ext/rustc_macro_registrar.rs @@ -273,5 +273,5 @@ fn mk_registrar(cx: &mut ExtCtxt, i }); - cx.expander().fold_item(module).pop().unwrap() + cx.monotonic_expander().fold_item(module).pop().unwrap() } -- cgit 1.4.1-3-g733a5