diff options
| author | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2019-08-24 21:12:13 +0300 |
|---|---|---|
| committer | Vadim Petrochenkov <vadim.petrochenkov@gmail.com> | 2019-08-27 00:31:55 +0300 |
| commit | ec45b87957c4158934fc3f5a821594ad0686ea4e (patch) | |
| tree | 59abaac544f9b95b121f12537476e11b62f7d8b7 /src/libsyntax/ext | |
| parent | 9b91b9c10e3c87ed333a1e34c4f46ed68f1eee06 (diff) | |
| download | rust-ec45b87957c4158934fc3f5a821594ad0686ea4e.tar.gz rust-ec45b87957c4158934fc3f5a821594ad0686ea4e.zip | |
resolve: Block expansion of a derive container until all its derives are resolved
Also mark derive helpers as known as a part of the derive container's expansion instead of expansion of the derives themselves which may happen too late.
Diffstat (limited to 'src/libsyntax/ext')
| -rw-r--r-- | src/libsyntax/ext/base.rs | 20 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 114 | ||||
| -rw-r--r-- | src/libsyntax/ext/proc_macro.rs | 6 |
3 files changed, 86 insertions, 54 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index a63c4181d5e..f0397558a1d 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -11,6 +11,7 @@ use crate::ptr::P; use crate::symbol::{kw, sym, Ident, Symbol}; use crate::{ThinVec, MACRO_ARGUMENTS}; use crate::tokenstream::{self, TokenStream, TokenTree}; +use crate::visit::Visitor; use errors::{DiagnosticBuilder, DiagnosticId}; use smallvec::{smallvec, SmallVec}; @@ -72,6 +73,17 @@ impl Annotatable { } } + pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) { + match self { + Annotatable::Item(item) => visitor.visit_item(item), + Annotatable::TraitItem(trait_item) => visitor.visit_trait_item(trait_item), + Annotatable::ImplItem(impl_item) => visitor.visit_impl_item(impl_item), + Annotatable::ForeignItem(foreign_item) => visitor.visit_foreign_item(foreign_item), + Annotatable::Stmt(stmt) => visitor.visit_stmt(stmt), + Annotatable::Expr(expr) => visitor.visit_expr(expr), + } + } + pub fn expect_item(self) -> P<ast::Item> { match self { Annotatable::Item(i) => i, @@ -637,6 +649,12 @@ impl SyntaxExtension { pub type NamedSyntaxExtension = (Name, SyntaxExtension); +/// Result of resolving a macro invocation. +pub enum InvocationRes { + Single(Lrc<SyntaxExtension>), + DeriveContainer(Vec<Lrc<SyntaxExtension>>), +} + /// Error type that denotes indeterminacy. pub struct Indeterminate; @@ -664,7 +682,7 @@ pub trait Resolver { fn resolve_macro_invocation( &mut self, invoc: &Invocation, eager_expansion_root: ExpnId, force: bool - ) -> Result<Option<Lrc<SyntaxExtension>>, Indeterminate>; + ) -> Result<InvocationRes, Indeterminate>; fn check_unused_macros(&self); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index a6b56e4d597..7b4a5167446 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -4,7 +4,7 @@ use crate::attr::{self, HasAttrs}; use crate::source_map::respan; use crate::config::StripUnconfigured; use crate::ext::base::*; -use crate::ext::proc_macro::collect_derives; +use crate::ext::proc_macro::{collect_derives, MarkAttrs}; use crate::ext::hygiene::{ExpnId, SyntaxContext, ExpnData, ExpnKind}; use crate::ext::tt::macro_rules::annotate_err_with_kind; use crate::ext::placeholders::{placeholder, PlaceholderExpander}; @@ -307,10 +307,10 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let eager_expansion_root = if self.monotonic { invoc.expansion_data.id } else { orig_expansion_data.id }; - let ext = match self.cx.resolver.resolve_macro_invocation( + let res = match self.cx.resolver.resolve_macro_invocation( &invoc, eager_expansion_root, force ) { - Ok(ext) => ext, + Ok(res) => res, Err(Indeterminate) => { undetermined_invocations.push(invoc); continue @@ -322,54 +322,72 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.current_expansion = invoc.expansion_data.clone(); // FIXME(jseyfried): Refactor out the following logic - let (expanded_fragment, new_invocations) = if let Some(ext) = ext { - let fragment = self.expand_invoc(invoc, &ext.kind); - self.collect_invocations(fragment, &[]) - } else if let InvocationKind::DeriveContainer { derives: traits, item } = invoc.kind { - if !item.derive_allowed() { - let attr = attr::find_by_name(item.attrs(), sym::derive) - .expect("`derive` attribute should exist"); - let span = attr.span; - let mut err = self.cx.mut_span_err(span, - "`derive` may only be applied to \ - structs, enums and unions"); - if let ast::AttrStyle::Inner = attr.style { - let trait_list = traits.iter() - .map(|t| t.to_string()).collect::<Vec<_>>(); - let suggestion = format!("#[derive({})]", trait_list.join(", ")); - err.span_suggestion( - span, "try an outer attribute", suggestion, - // We don't 𝑘𝑛𝑜𝑤 that the following item is an ADT - Applicability::MaybeIncorrect - ); - } - err.emit(); + let (expanded_fragment, new_invocations) = match res { + InvocationRes::Single(ext) => { + let fragment = self.expand_invoc(invoc, &ext.kind); + self.collect_invocations(fragment, &[]) } + InvocationRes::DeriveContainer(exts) => { + let (derives, item) = match invoc.kind { + InvocationKind::DeriveContainer { derives, item } => (derives, item), + _ => unreachable!(), + }; + if !item.derive_allowed() { + let attr = attr::find_by_name(item.attrs(), sym::derive) + .expect("`derive` attribute should exist"); + let span = attr.span; + let mut err = self.cx.mut_span_err(span, + "`derive` may only be applied to structs, enums and unions"); + if let ast::AttrStyle::Inner = attr.style { + let trait_list = derives.iter() + .map(|t| t.to_string()).collect::<Vec<_>>(); + let suggestion = format!("#[derive({})]", trait_list.join(", ")); + err.span_suggestion( + span, "try an outer attribute", suggestion, + // We don't 𝑘𝑛𝑜𝑤 that the following item is an ADT + Applicability::MaybeIncorrect + ); + } + err.emit(); + } - let mut item = self.fully_configure(item); - item.visit_attrs(|attrs| attrs.retain(|a| a.path != sym::derive)); - let derive_placeholders = - all_derive_placeholders.entry(invoc.expansion_data.id).or_default(); - - derive_placeholders.reserve(traits.len()); - invocations.reserve(traits.len()); - for path in traits { - let expn_id = ExpnId::fresh(None); - derive_placeholders.push(NodeId::placeholder_from_expn_id(expn_id)); - invocations.push(Invocation { - kind: InvocationKind::Derive { path, item: item.clone() }, - fragment_kind: invoc.fragment_kind, - expansion_data: ExpansionData { - id: expn_id, - ..invoc.expansion_data.clone() - }, - }); + let mut item = self.fully_configure(item); + item.visit_attrs(|attrs| attrs.retain(|a| a.path != sym::derive)); + let mut helper_attrs = Vec::new(); + let mut has_copy = false; + for ext in exts { + helper_attrs.extend(&ext.helper_attrs); + has_copy |= ext.is_derive_copy; + } + // Mark derive helpers inside this item as known and used. + // FIXME: This is a hack, derive helpers should be integrated with regular name + // resolution instead. For example, helpers introduced by a derive container + // can be in scope for all code produced by that container's expansion. + item.visit_with(&mut MarkAttrs(&helper_attrs)); + if has_copy { + self.cx.resolver.add_derives(invoc.expansion_data.id, SpecialDerives::COPY); + } + + let derive_placeholders = + all_derive_placeholders.entry(invoc.expansion_data.id).or_default(); + derive_placeholders.reserve(derives.len()); + invocations.reserve(derives.len()); + for path in derives { + let expn_id = ExpnId::fresh(None); + derive_placeholders.push(NodeId::placeholder_from_expn_id(expn_id)); + invocations.push(Invocation { + kind: InvocationKind::Derive { path, item: item.clone() }, + fragment_kind: invoc.fragment_kind, + expansion_data: ExpansionData { + id: expn_id, + ..invoc.expansion_data.clone() + }, + }); + } + let fragment = invoc.fragment_kind + .expect_from_annotatables(::std::iter::once(item)); + self.collect_invocations(fragment, derive_placeholders) } - let fragment = invoc.fragment_kind - .expect_from_annotatables(::std::iter::once(item)); - self.collect_invocations(fragment, derive_placeholders) - } else { - unreachable!() }; if expanded_fragments.len() < depth { diff --git a/src/libsyntax/ext/proc_macro.rs b/src/libsyntax/ext/proc_macro.rs index c17b6f6b424..4a44c9a9f1f 100644 --- a/src/libsyntax/ext/proc_macro.rs +++ b/src/libsyntax/ext/proc_macro.rs @@ -78,7 +78,6 @@ pub struct ProcMacroDerive { pub client: proc_macro::bridge::client::Client< fn(proc_macro::TokenStream) -> proc_macro::TokenStream, >, - pub attrs: Vec<ast::Name>, } impl MultiItemModifier for ProcMacroDerive { @@ -111,9 +110,6 @@ impl MultiItemModifier for ProcMacroDerive { } } - // Mark attributes as known, and used. - MarkAttrs(&self.attrs).visit_item(&item); - let token = token::Interpolated(Lrc::new(token::NtItem(item))); let input = tokenstream::TokenTree::token(token, DUMMY_SP).into(); @@ -164,7 +160,7 @@ impl MultiItemModifier for ProcMacroDerive { } } -struct MarkAttrs<'a>(&'a [ast::Name]); +crate struct MarkAttrs<'a>(crate &'a [ast::Name]); impl<'a> Visitor<'a> for MarkAttrs<'a> { fn visit_attribute(&mut self, attr: &Attribute) { |
