diff options
Diffstat (limited to 'compiler/rustc_expand/src/expand.rs')
| -rw-r--r-- | compiler/rustc_expand/src/expand.rs | 1812 |
1 files changed, 1812 insertions, 0 deletions
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs new file mode 100644 index 00000000000..7a21caf255a --- /dev/null +++ b/compiler/rustc_expand/src/expand.rs @@ -0,0 +1,1812 @@ +use crate::base::*; +use crate::config::StripUnconfigured; +use crate::configure; +use crate::hygiene::{ExpnData, ExpnKind, SyntaxContext}; +use crate::mbe::macro_rules::annotate_err_with_kind; +use crate::module::{parse_external_mod, push_directory, Directory, DirectoryOwnership}; +use crate::placeholders::{placeholder, PlaceholderExpander}; +use crate::proc_macro::collect_derives; + +use rustc_ast::mut_visit::*; +use rustc_ast::ptr::P; +use rustc_ast::token; +use rustc_ast::tokenstream::TokenStream; +use rustc_ast::visit::{self, AssocCtxt, Visitor}; +use rustc_ast::{self as ast, AttrItem, Block, LitKind, NodeId, PatKind, Path}; +use rustc_ast::{ItemKind, MacArgs, MacStmtStyle, StmtKind}; +use rustc_ast_pretty::pprust; +use rustc_attr::{self as attr, is_builtin_attr, HasAttrs}; +use rustc_data_structures::map_in_place::MapInPlace; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::{Applicability, PResult}; +use rustc_feature::Features; +use rustc_parse::parser::Parser; +use rustc_parse::validate_attr; +use rustc_session::lint::builtin::UNUSED_DOC_COMMENTS; +use rustc_session::lint::BuiltinLintDiagnostics; +use rustc_session::parse::{feature_err, ParseSess}; +use rustc_session::Limit; +use rustc_span::source_map::respan; +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::{ExpnId, FileName, Span, DUMMY_SP}; + +use smallvec::{smallvec, SmallVec}; +use std::io::ErrorKind; +use std::ops::DerefMut; +use std::path::PathBuf; +use std::rc::Rc; +use std::{iter, mem, slice}; + +macro_rules! ast_fragments { + ( + $($Kind:ident($AstTy:ty) { + $kind_name:expr; + $(one fn $mut_visit_ast:ident; fn $visit_ast:ident;)? + $(many fn $flat_map_ast_elt:ident; fn $visit_ast_elt:ident($($args:tt)*);)? + fn $make_ast:ident; + })* + ) => { + /// A fragment of AST that can be produced by a single macro expansion. + /// Can also serve as an input and intermediate result for macro expansion operations. + pub enum AstFragment { + OptExpr(Option<P<ast::Expr>>), + $($Kind($AstTy),)* + } + + /// "Discriminant" of an AST fragment. + #[derive(Copy, Clone, PartialEq, Eq)] + pub enum AstFragmentKind { + OptExpr, + $($Kind,)* + } + + impl AstFragmentKind { + pub fn name(self) -> &'static str { + match self { + AstFragmentKind::OptExpr => "expression", + $(AstFragmentKind::$Kind => $kind_name,)* + } + } + + fn make_from<'a>(self, result: Box<dyn MacResult + 'a>) -> Option<AstFragment> { + match self { + AstFragmentKind::OptExpr => + result.make_expr().map(Some).map(AstFragment::OptExpr), + $(AstFragmentKind::$Kind => result.$make_ast().map(AstFragment::$Kind),)* + } + } + } + + impl AstFragment { + pub fn add_placeholders(&mut self, placeholders: &[NodeId]) { + if placeholders.is_empty() { + return; + } + match self { + $($(AstFragment::$Kind(ast) => ast.extend(placeholders.iter().flat_map(|id| { + // We are repeating through arguments with `many`, to do that we have to + // mention some macro variable from those arguments even if it's not used. + macro _repeating($flat_map_ast_elt) {} + placeholder(AstFragmentKind::$Kind, *id, None).$make_ast() + })),)?)* + _ => panic!("unexpected AST fragment kind") + } + } + + pub fn make_opt_expr(self) -> Option<P<ast::Expr>> { + match self { + AstFragment::OptExpr(expr) => expr, + _ => panic!("AstFragment::make_* called on the wrong kind of fragment"), + } + } + + $(pub fn $make_ast(self) -> $AstTy { + match self { + AstFragment::$Kind(ast) => ast, + _ => panic!("AstFragment::make_* called on the wrong kind of fragment"), + } + })* + + pub fn mut_visit_with<F: MutVisitor>(&mut self, vis: &mut F) { + match self { + AstFragment::OptExpr(opt_expr) => { + visit_clobber(opt_expr, |opt_expr| { + if let Some(expr) = opt_expr { + vis.filter_map_expr(expr) + } else { + None + } + }); + } + $($(AstFragment::$Kind(ast) => vis.$mut_visit_ast(ast),)?)* + $($(AstFragment::$Kind(ast) => + ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast)),)?)* + } + } + + pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) { + match *self { + AstFragment::OptExpr(Some(ref expr)) => visitor.visit_expr(expr), + AstFragment::OptExpr(None) => {} + $($(AstFragment::$Kind(ref ast) => visitor.$visit_ast(ast),)?)* + $($(AstFragment::$Kind(ref ast) => for ast_elt in &ast[..] { + visitor.$visit_ast_elt(ast_elt, $($args)*); + })?)* + } + } + } + + impl<'a> MacResult for crate::mbe::macro_rules::ParserAnyMacro<'a> { + $(fn $make_ast(self: Box<crate::mbe::macro_rules::ParserAnyMacro<'a>>) + -> Option<$AstTy> { + Some(self.make(AstFragmentKind::$Kind).$make_ast()) + })* + } + } +} + +ast_fragments! { + Expr(P<ast::Expr>) { "expression"; one fn visit_expr; fn visit_expr; fn make_expr; } + Pat(P<ast::Pat>) { "pattern"; one fn visit_pat; fn visit_pat; fn make_pat; } + Ty(P<ast::Ty>) { "type"; one fn visit_ty; fn visit_ty; fn make_ty; } + Stmts(SmallVec<[ast::Stmt; 1]>) { + "statement"; many fn flat_map_stmt; fn visit_stmt(); fn make_stmts; + } + Items(SmallVec<[P<ast::Item>; 1]>) { + "item"; many fn flat_map_item; fn visit_item(); fn make_items; + } + TraitItems(SmallVec<[P<ast::AssocItem>; 1]>) { + "trait item"; + many fn flat_map_trait_item; + fn visit_assoc_item(AssocCtxt::Trait); + fn make_trait_items; + } + ImplItems(SmallVec<[P<ast::AssocItem>; 1]>) { + "impl item"; + many fn flat_map_impl_item; + fn visit_assoc_item(AssocCtxt::Impl); + fn make_impl_items; + } + ForeignItems(SmallVec<[P<ast::ForeignItem>; 1]>) { + "foreign item"; + many fn flat_map_foreign_item; + fn visit_foreign_item(); + fn make_foreign_items; + } + Arms(SmallVec<[ast::Arm; 1]>) { + "match arm"; many fn flat_map_arm; fn visit_arm(); fn make_arms; + } + Fields(SmallVec<[ast::Field; 1]>) { + "field expression"; many fn flat_map_field; fn visit_field(); fn make_fields; + } + FieldPats(SmallVec<[ast::FieldPat; 1]>) { + "field pattern"; + many fn flat_map_field_pattern; + fn visit_field_pattern(); + fn make_field_patterns; + } + GenericParams(SmallVec<[ast::GenericParam; 1]>) { + "generic parameter"; + many fn flat_map_generic_param; + fn visit_generic_param(); + fn make_generic_params; + } + Params(SmallVec<[ast::Param; 1]>) { + "function parameter"; many fn flat_map_param; fn visit_param(); fn make_params; + } + StructFields(SmallVec<[ast::StructField; 1]>) { + "field"; + many fn flat_map_struct_field; + fn visit_struct_field(); + fn make_struct_fields; + } + Variants(SmallVec<[ast::Variant; 1]>) { + "variant"; many fn flat_map_variant; fn visit_variant(); fn make_variants; + } +} + +impl AstFragmentKind { + crate fn dummy(self, span: Span) -> AstFragment { + self.make_from(DummyResult::any(span)).expect("couldn't create a dummy AST fragment") + } + + fn expect_from_annotatables<I: IntoIterator<Item = Annotatable>>( + self, + items: I, + ) -> AstFragment { + let mut items = items.into_iter(); + match self { + AstFragmentKind::Arms => { + AstFragment::Arms(items.map(Annotatable::expect_arm).collect()) + } + AstFragmentKind::Fields => { + AstFragment::Fields(items.map(Annotatable::expect_field).collect()) + } + AstFragmentKind::FieldPats => { + AstFragment::FieldPats(items.map(Annotatable::expect_field_pattern).collect()) + } + AstFragmentKind::GenericParams => { + AstFragment::GenericParams(items.map(Annotatable::expect_generic_param).collect()) + } + AstFragmentKind::Params => { + AstFragment::Params(items.map(Annotatable::expect_param).collect()) + } + AstFragmentKind::StructFields => { + AstFragment::StructFields(items.map(Annotatable::expect_struct_field).collect()) + } + AstFragmentKind::Variants => { + AstFragment::Variants(items.map(Annotatable::expect_variant).collect()) + } + AstFragmentKind::Items => { + AstFragment::Items(items.map(Annotatable::expect_item).collect()) + } + AstFragmentKind::ImplItems => { + AstFragment::ImplItems(items.map(Annotatable::expect_impl_item).collect()) + } + AstFragmentKind::TraitItems => { + AstFragment::TraitItems(items.map(Annotatable::expect_trait_item).collect()) + } + AstFragmentKind::ForeignItems => { + AstFragment::ForeignItems(items.map(Annotatable::expect_foreign_item).collect()) + } + AstFragmentKind::Stmts => { + AstFragment::Stmts(items.map(Annotatable::expect_stmt).collect()) + } + AstFragmentKind::Expr => AstFragment::Expr( + items.next().expect("expected exactly one expression").expect_expr(), + ), + AstFragmentKind::OptExpr => { + AstFragment::OptExpr(items.next().map(Annotatable::expect_expr)) + } + AstFragmentKind::Pat | AstFragmentKind::Ty => { + panic!("patterns and types aren't annotatable") + } + } + } +} + +pub struct Invocation { + pub kind: InvocationKind, + pub fragment_kind: AstFragmentKind, + pub expansion_data: ExpansionData, +} + +pub enum InvocationKind { + Bang { + mac: ast::MacCall, + span: Span, + }, + Attr { + attr: ast::Attribute, + item: Annotatable, + // Required for resolving derive helper attributes. + derives: Vec<Path>, + // We temporarily report errors for attribute macros placed after derives + after_derive: bool, + }, + Derive { + path: Path, + item: Annotatable, + }, + /// "Invocation" that contains all derives from an item, + /// broken into multiple `Derive` invocations when expanded. + /// FIXME: Find a way to remove it. + DeriveContainer { + derives: Vec<Path>, + item: Annotatable, + }, +} + +impl InvocationKind { + fn placeholder_visibility(&self) -> Option<ast::Visibility> { + // HACK: For unnamed fields placeholders should have the same visibility as the actual + // fields because for tuple structs/variants resolve determines visibilities of their + // constructor using these field visibilities before attributes on them are are expanded. + // The assumption is that the attribute expansion cannot change field visibilities, + // and it holds because only inert attributes are supported in this position. + match self { + InvocationKind::Attr { item: Annotatable::StructField(field), .. } + | InvocationKind::Derive { item: Annotatable::StructField(field), .. } + | InvocationKind::DeriveContainer { item: Annotatable::StructField(field), .. } + if field.ident.is_none() => + { + Some(field.vis.clone()) + } + _ => None, + } + } +} + +impl Invocation { + pub fn span(&self) -> Span { + match &self.kind { + InvocationKind::Bang { span, .. } => *span, + InvocationKind::Attr { attr, .. } => attr.span, + InvocationKind::Derive { path, .. } => path.span, + InvocationKind::DeriveContainer { item, .. } => item.span(), + } + } +} + +pub struct MacroExpander<'a, 'b> { + pub cx: &'a mut ExtCtxt<'b>, + monotonic: bool, // cf. `cx.monotonic_expander()` +} + +impl<'a, 'b> MacroExpander<'a, 'b> { + pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self { + MacroExpander { cx, monotonic } + } + + pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate { + let mut module = ModuleData { + mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)], + directory: match self.cx.source_map().span_to_unmapped_path(krate.span) { + FileName::Real(name) => name.into_local_path(), + other => PathBuf::from(other.to_string()), + }, + }; + module.directory.pop(); + self.cx.root_path = module.directory.clone(); + self.cx.current_expansion.module = Rc::new(module); + + let orig_mod_span = krate.module.inner; + + let krate_item = AstFragment::Items(smallvec![P(ast::Item { + attrs: krate.attrs, + span: krate.span, + kind: ast::ItemKind::Mod(krate.module), + ident: Ident::invalid(), + id: ast::DUMMY_NODE_ID, + vis: respan(krate.span.shrink_to_lo(), ast::VisibilityKind::Public), + tokens: None, + })]); + + match self.fully_expand_fragment(krate_item).make_items().pop().map(P::into_inner) { + Some(ast::Item { attrs, kind: ast::ItemKind::Mod(module), .. }) => { + krate.attrs = attrs; + krate.module = module; + } + None => { + // Resolution failed so we return an empty expansion + krate.attrs = vec![]; + krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true }; + } + Some(ast::Item { span, kind, .. }) => { + krate.attrs = vec![]; + krate.module = ast::Mod { inner: orig_mod_span, items: vec![], inline: true }; + self.cx.span_err( + span, + &format!( + "expected crate top-level item to be a module after macro expansion, found {} {}", + kind.article(), kind.descr() + ), + ); + } + }; + self.cx.trace_macros_diag(); + krate + } + + // Recursively expand all macro invocations in this AST fragment. + pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragment { + let orig_expansion_data = self.cx.current_expansion.clone(); + self.cx.current_expansion.depth = 0; + + // Collect all macro invocations and replace them with placeholders. + let (mut fragment_with_placeholders, mut invocations) = + self.collect_invocations(input_fragment, &[]); + + // Optimization: if we resolve all imports now, + // we'll be able to immediately resolve most of imported macros. + self.resolve_imports(); + + // Resolve paths in all invocations and produce output expanded fragments for them, but + // do not insert them into our input AST fragment yet, only store in `expanded_fragments`. + // The output fragments also go through expansion recursively until no invocations are left. + // Unresolved macros produce dummy outputs as a recovery measure. + invocations.reverse(); + let mut expanded_fragments = Vec::new(); + let mut undetermined_invocations = Vec::new(); + let (mut progress, mut force) = (false, !self.monotonic); + loop { + let (invoc, res) = if let Some(invoc) = invocations.pop() { + invoc + } else { + self.resolve_imports(); + if undetermined_invocations.is_empty() { + break; + } + invocations = mem::take(&mut undetermined_invocations); + force = !mem::replace(&mut progress, false); + continue; + }; + + let res = match res { + Some(res) => res, + None => { + let eager_expansion_root = if self.monotonic { + invoc.expansion_data.id + } else { + orig_expansion_data.id + }; + match self.cx.resolver.resolve_macro_invocation( + &invoc, + eager_expansion_root, + force, + ) { + Ok(res) => res, + Err(Indeterminate) => { + // Cannot resolve, will retry this invocation later. + undetermined_invocations.push((invoc, None)); + continue; + } + } + } + }; + + let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data; + self.cx.current_expansion = invoc.expansion_data.clone(); + + // FIXME(jseyfried): Refactor out the following logic + let (expanded_fragment, new_invocations) = match res { + InvocationRes::Single(ext) => match self.expand_invoc(invoc, &ext.kind) { + ExpandResult::Ready(fragment) => self.collect_invocations(fragment, &[]), + ExpandResult::Retry(invoc, explanation) => { + if force { + // We are stuck, stop retrying and produce a dummy fragment. + let span = invoc.span(); + self.cx.span_err(span, &explanation); + let fragment = invoc.fragment_kind.dummy(span); + self.collect_invocations(fragment, &[]) + } else { + // Cannot expand, will retry this invocation later. + undetermined_invocations + .push((invoc, Some(InvocationRes::Single(ext)))); + continue; + } + } + }, + InvocationRes::DeriveContainer(_exts) => { + // FIXME: Consider using the derive resolutions (`_exts`) immediately, + // instead of enqueuing the derives to be resolved again later. + let (derives, item) = match invoc.kind { + InvocationKind::DeriveContainer { derives, item } => (derives, item), + _ => unreachable!(), + }; + if !item.derive_allowed() { + self.error_derive_forbidden_on_non_adt(&derives, &item); + } + + let mut item = self.fully_configure(item); + item.visit_attrs(|attrs| attrs.retain(|a| !a.has_name(sym::derive))); + + let mut derive_placeholders = Vec::with_capacity(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() + }, + }, + None, + )); + } + let fragment = + invoc.fragment_kind.expect_from_annotatables(::std::iter::once(item)); + self.collect_invocations(fragment, &derive_placeholders) + } + }; + + progress = true; + if expanded_fragments.len() < depth { + expanded_fragments.push(Vec::new()); + } + expanded_fragments[depth - 1].push((expn_id, expanded_fragment)); + invocations.extend(new_invocations.into_iter().rev()); + } + + self.cx.current_expansion = orig_expansion_data; + + // Finally incorporate all the expanded macros into the input AST fragment. + let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic); + while let Some(expanded_fragments) = expanded_fragments.pop() { + for (expn_id, expanded_fragment) in expanded_fragments.into_iter().rev() { + placeholder_expander + .add(NodeId::placeholder_from_expn_id(expn_id), expanded_fragment); + } + } + fragment_with_placeholders.mut_visit_with(&mut placeholder_expander); + fragment_with_placeholders + } + + fn error_derive_forbidden_on_non_adt(&self, derives: &[Path], item: &Annotatable) { + let attr = self.cx.sess.find_by_name(item.attrs(), sym::derive); + let span = attr.map_or(item.span(), |attr| attr.span); + let mut err = self + .cx + .struct_span_err(span, "`derive` may only be applied to structs, enums and unions"); + if let Some(ast::Attribute { style: ast::AttrStyle::Inner, .. }) = attr { + let trait_list = derives.iter().map(|t| pprust::path_to_string(t)).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(); + } + + fn resolve_imports(&mut self) { + if self.monotonic { + self.cx.resolver.resolve_imports(); + } + } + + /// Collects all macro invocations reachable at this time in this AST fragment, and replace + /// them with "placeholders" - dummy macro invocations with specially crafted `NodeId`s. + /// Then call into resolver that builds a skeleton ("reduced graph") of the fragment and + /// prepares data for resolving paths of macro invocations. + fn collect_invocations( + &mut self, + mut fragment: AstFragment, + extra_placeholders: &[NodeId], + ) -> (AstFragment, Vec<(Invocation, Option<InvocationRes>)>) { + // Resolve `$crate`s in the fragment for pretty-printing. + self.cx.resolver.resolve_dollar_crates(); + + let invocations = { + let mut collector = InvocationCollector { + cfg: StripUnconfigured { sess: &self.cx.sess, features: self.cx.ecfg.features }, + cx: self.cx, + invocations: Vec::new(), + monotonic: self.monotonic, + }; + fragment.mut_visit_with(&mut collector); + fragment.add_placeholders(extra_placeholders); + collector.invocations + }; + + if self.monotonic { + self.cx + .resolver + .visit_ast_fragment_with_placeholders(self.cx.current_expansion.id, &fragment); + } + + (fragment, invocations) + } + + fn fully_configure(&mut self, item: Annotatable) -> Annotatable { + let mut cfg = StripUnconfigured { sess: &self.cx.sess, features: self.cx.ecfg.features }; + // Since the item itself has already been configured by the InvocationCollector, + // we know that fold result vector will contain exactly one element + match item { + Annotatable::Item(item) => Annotatable::Item(cfg.flat_map_item(item).pop().unwrap()), + Annotatable::TraitItem(item) => { + Annotatable::TraitItem(cfg.flat_map_trait_item(item).pop().unwrap()) + } + Annotatable::ImplItem(item) => { + Annotatable::ImplItem(cfg.flat_map_impl_item(item).pop().unwrap()) + } + Annotatable::ForeignItem(item) => { + Annotatable::ForeignItem(cfg.flat_map_foreign_item(item).pop().unwrap()) + } + Annotatable::Stmt(stmt) => { + Annotatable::Stmt(stmt.map(|stmt| cfg.flat_map_stmt(stmt).pop().unwrap())) + } + Annotatable::Expr(mut expr) => Annotatable::Expr({ + cfg.visit_expr(&mut expr); + expr + }), + Annotatable::Arm(arm) => Annotatable::Arm(cfg.flat_map_arm(arm).pop().unwrap()), + Annotatable::Field(field) => { + Annotatable::Field(cfg.flat_map_field(field).pop().unwrap()) + } + Annotatable::FieldPat(fp) => { + Annotatable::FieldPat(cfg.flat_map_field_pattern(fp).pop().unwrap()) + } + Annotatable::GenericParam(param) => { + Annotatable::GenericParam(cfg.flat_map_generic_param(param).pop().unwrap()) + } + Annotatable::Param(param) => { + Annotatable::Param(cfg.flat_map_param(param).pop().unwrap()) + } + Annotatable::StructField(sf) => { + Annotatable::StructField(cfg.flat_map_struct_field(sf).pop().unwrap()) + } + Annotatable::Variant(v) => Annotatable::Variant(cfg.flat_map_variant(v).pop().unwrap()), + } + } + + fn error_recursion_limit_reached(&mut self) { + let expn_data = self.cx.current_expansion.id.expn_data(); + let suggested_limit = self.cx.ecfg.recursion_limit * 2; + self.cx + .struct_span_err( + expn_data.call_site, + &format!("recursion limit reached while expanding `{}`", expn_data.kind.descr()), + ) + .help(&format!( + "consider adding a `#![recursion_limit=\"{}\"]` attribute to your crate (`{}`)", + suggested_limit, self.cx.ecfg.crate_name, + )) + .emit(); + self.cx.trace_macros_diag(); + } + + /// A macro's expansion does not fit in this fragment kind. + /// For example, a non-type macro in a type position. + fn error_wrong_fragment_kind(&mut self, kind: AstFragmentKind, mac: &ast::MacCall, span: Span) { + let msg = format!( + "non-{kind} macro in {kind} position: {path}", + kind = kind.name(), + path = pprust::path_to_string(&mac.path), + ); + self.cx.span_err(span, &msg); + self.cx.trace_macros_diag(); + } + + fn expand_invoc( + &mut self, + invoc: Invocation, + ext: &SyntaxExtensionKind, + ) -> ExpandResult<AstFragment, Invocation> { + let recursion_limit = + self.cx.reduced_recursion_limit.unwrap_or(self.cx.ecfg.recursion_limit); + if !recursion_limit.value_within_limit(self.cx.current_expansion.depth) { + if self.cx.reduced_recursion_limit.is_none() { + self.error_recursion_limit_reached(); + } + + // Reduce the recursion limit by half each time it triggers. + self.cx.reduced_recursion_limit = Some(recursion_limit / 2); + + return ExpandResult::Ready(invoc.fragment_kind.dummy(invoc.span())); + } + + let (fragment_kind, span) = (invoc.fragment_kind, invoc.span()); + ExpandResult::Ready(match invoc.kind { + InvocationKind::Bang { mac, .. } => match ext { + SyntaxExtensionKind::Bang(expander) => { + let tok_result = match expander.expand(self.cx, span, mac.args.inner_tokens()) { + Err(_) => return ExpandResult::Ready(fragment_kind.dummy(span)), + Ok(ts) => ts, + }; + self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span) + } + SyntaxExtensionKind::LegacyBang(expander) => { + let prev = self.cx.current_expansion.prior_type_ascription; + self.cx.current_expansion.prior_type_ascription = mac.prior_type_ascription; + let tok_result = expander.expand(self.cx, span, mac.args.inner_tokens()); + let result = if let Some(result) = fragment_kind.make_from(tok_result) { + result + } else { + self.error_wrong_fragment_kind(fragment_kind, &mac, span); + fragment_kind.dummy(span) + }; + self.cx.current_expansion.prior_type_ascription = prev; + result + } + _ => unreachable!(), + }, + InvocationKind::Attr { attr, mut item, derives, after_derive } => match ext { + SyntaxExtensionKind::Attr(expander) => { + self.gate_proc_macro_input(&item); + self.gate_proc_macro_attr_item(span, &item); + let tokens = item.into_tokens(&self.cx.sess.parse_sess); + let attr_item = attr.unwrap_normal_item(); + if let MacArgs::Eq(..) = attr_item.args { + self.cx.span_err(span, "key-value macro attributes are not supported"); + } + let inner_tokens = attr_item.args.inner_tokens(); + let tok_result = match expander.expand(self.cx, span, inner_tokens, tokens) { + Err(_) => return ExpandResult::Ready(fragment_kind.dummy(span)), + Ok(ts) => ts, + }; + self.parse_ast_fragment(tok_result, fragment_kind, &attr_item.path, span) + } + SyntaxExtensionKind::LegacyAttr(expander) => { + match validate_attr::parse_meta(&self.cx.sess.parse_sess, &attr) { + Ok(meta) => { + let items = match expander.expand(self.cx, span, &meta, item) { + ExpandResult::Ready(items) => items, + ExpandResult::Retry(item, explanation) => { + // Reassemble the original invocation for retrying. + return ExpandResult::Retry( + Invocation { + kind: InvocationKind::Attr { + attr, + item, + derives, + after_derive, + }, + ..invoc + }, + explanation, + ); + } + }; + fragment_kind.expect_from_annotatables(items) + } + Err(mut err) => { + err.emit(); + fragment_kind.dummy(span) + } + } + } + SyntaxExtensionKind::NonMacroAttr { mark_used } => { + self.cx.sess.mark_attr_known(&attr); + if *mark_used { + self.cx.sess.mark_attr_used(&attr); + } + item.visit_attrs(|attrs| attrs.push(attr)); + fragment_kind.expect_from_annotatables(iter::once(item)) + } + _ => unreachable!(), + }, + InvocationKind::Derive { path, item } => match ext { + SyntaxExtensionKind::Derive(expander) + | SyntaxExtensionKind::LegacyDerive(expander) => { + if !item.derive_allowed() { + return ExpandResult::Ready(fragment_kind.dummy(span)); + } + if let SyntaxExtensionKind::Derive(..) = ext { + self.gate_proc_macro_input(&item); + } + let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path }; + let items = match expander.expand(self.cx, span, &meta, item) { + ExpandResult::Ready(items) => items, + ExpandResult::Retry(item, explanation) => { + // Reassemble the original invocation for retrying. + return ExpandResult::Retry( + Invocation { + kind: InvocationKind::Derive { path: meta.path, item }, + ..invoc + }, + explanation, + ); + } + }; + fragment_kind.expect_from_annotatables(items) + } + _ => unreachable!(), + }, + InvocationKind::DeriveContainer { .. } => unreachable!(), + }) + } + + fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) { + let kind = match item { + Annotatable::Item(_) + | Annotatable::TraitItem(_) + | Annotatable::ImplItem(_) + | Annotatable::ForeignItem(_) => return, + Annotatable::Stmt(_) => "statements", + Annotatable::Expr(_) => "expressions", + Annotatable::Arm(..) + | Annotatable::Field(..) + | Annotatable::FieldPat(..) + | Annotatable::GenericParam(..) + | Annotatable::Param(..) + | Annotatable::StructField(..) + | Annotatable::Variant(..) => panic!("unexpected annotatable"), + }; + if self.cx.ecfg.proc_macro_hygiene() { + return; + } + feature_err( + &self.cx.sess.parse_sess, + sym::proc_macro_hygiene, + span, + &format!("custom attributes cannot be applied to {}", kind), + ) + .emit(); + } + + fn gate_proc_macro_input(&self, annotatable: &Annotatable) { + struct GateProcMacroInput<'a> { + parse_sess: &'a ParseSess, + } + + impl<'ast, 'a> Visitor<'ast> for GateProcMacroInput<'a> { + fn visit_item(&mut self, item: &'ast ast::Item) { + match &item.kind { + ast::ItemKind::Mod(module) if !module.inline => { + feature_err( + self.parse_sess, + sym::proc_macro_hygiene, + item.span, + "non-inline modules in proc macro input are unstable", + ) + .emit(); + } + _ => {} + } + + visit::walk_item(self, item); + } + + fn visit_mac(&mut self, _: &'ast ast::MacCall) {} + } + + if !self.cx.ecfg.proc_macro_hygiene() { + annotatable + .visit_with(&mut GateProcMacroInput { parse_sess: &self.cx.sess.parse_sess }); + } + } + + fn parse_ast_fragment( + &mut self, + toks: TokenStream, + kind: AstFragmentKind, + path: &Path, + span: Span, + ) -> AstFragment { + let mut parser = self.cx.new_parser_from_tts(toks); + match parse_ast_fragment(&mut parser, kind) { + Ok(fragment) => { + ensure_complete_parse(&mut parser, path, kind.name(), span); + fragment + } + Err(mut err) => { + err.set_span(span); + annotate_err_with_kind(&mut err, kind, span); + err.emit(); + self.cx.trace_macros_diag(); + kind.dummy(span) + } + } + } +} + +pub fn parse_ast_fragment<'a>( + this: &mut Parser<'a>, + kind: AstFragmentKind, +) -> PResult<'a, AstFragment> { + Ok(match kind { + AstFragmentKind::Items => { + let mut items = SmallVec::new(); + while let Some(item) = this.parse_item()? { + items.push(item); + } + AstFragment::Items(items) + } + AstFragmentKind::TraitItems => { + let mut items = SmallVec::new(); + while let Some(item) = this.parse_trait_item()? { + items.extend(item); + } + AstFragment::TraitItems(items) + } + AstFragmentKind::ImplItems => { + let mut items = SmallVec::new(); + while let Some(item) = this.parse_impl_item()? { + items.extend(item); + } + AstFragment::ImplItems(items) + } + AstFragmentKind::ForeignItems => { + let mut items = SmallVec::new(); + while let Some(item) = this.parse_foreign_item()? { + items.extend(item); + } + AstFragment::ForeignItems(items) + } + AstFragmentKind::Stmts => { + let mut stmts = SmallVec::new(); + // Won't make progress on a `}`. + while this.token != token::Eof && this.token != token::CloseDelim(token::Brace) { + if let Some(stmt) = this.parse_full_stmt()? { + stmts.push(stmt); + } + } + AstFragment::Stmts(stmts) + } + AstFragmentKind::Expr => AstFragment::Expr(this.parse_expr()?), + AstFragmentKind::OptExpr => { + if this.token != token::Eof { + AstFragment::OptExpr(Some(this.parse_expr()?)) + } else { + AstFragment::OptExpr(None) + } + } + AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?), + AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat(None)?), + AstFragmentKind::Arms + | AstFragmentKind::Fields + | AstFragmentKind::FieldPats + | AstFragmentKind::GenericParams + | AstFragmentKind::Params + | AstFragmentKind::StructFields + | AstFragmentKind::Variants => panic!("unexpected AST fragment kind"), + }) +} + +pub fn ensure_complete_parse<'a>( + this: &mut Parser<'a>, + macro_path: &Path, + kind_name: &str, + span: Span, +) { + if this.token != token::Eof { + let token = pprust::token_to_string(&this.token); + let msg = format!("macro expansion ignores token `{}` and any following", token); + // Avoid emitting backtrace info twice. + let def_site_span = this.token.span.with_ctxt(SyntaxContext::root()); + let mut err = this.struct_span_err(def_site_span, &msg); + err.span_label(span, "caused by the macro expansion here"); + let msg = format!( + "the usage of `{}!` is likely invalid in {} context", + pprust::path_to_string(macro_path), + kind_name, + ); + err.note(&msg); + let semi_span = this.sess.source_map().next_point(span); + + let semi_full_span = semi_span.to(this.sess.source_map().next_point(semi_span)); + match this.sess.source_map().span_to_snippet(semi_full_span) { + Ok(ref snippet) if &snippet[..] != ";" && kind_name == "expression" => { + err.span_suggestion( + semi_span, + "you might be missing a semicolon here", + ";".to_owned(), + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + err.emit(); + } +} + +struct InvocationCollector<'a, 'b> { + cx: &'a mut ExtCtxt<'b>, + cfg: StripUnconfigured<'a>, + invocations: Vec<(Invocation, Option<InvocationRes>)>, + monotonic: bool, +} + +impl<'a, 'b> InvocationCollector<'a, 'b> { + fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment { + // Expansion data for all the collected invocations is set upon their resolution, + // with exception of the derive container case which is not resolved and can get + // its expansion data immediately. + let expn_data = match &kind { + InvocationKind::DeriveContainer { item, .. } => Some(ExpnData { + parent: self.cx.current_expansion.id, + ..ExpnData::default( + ExpnKind::Macro(MacroKind::Attr, sym::derive), + item.span(), + self.cx.sess.parse_sess.edition, + None, + ) + }), + _ => None, + }; + let expn_id = ExpnId::fresh(expn_data); + let vis = kind.placeholder_visibility(); + self.invocations.push(( + Invocation { + kind, + fragment_kind, + expansion_data: ExpansionData { + id: expn_id, + depth: self.cx.current_expansion.depth + 1, + ..self.cx.current_expansion.clone() + }, + }, + None, + )); + placeholder(fragment_kind, NodeId::placeholder_from_expn_id(expn_id), vis) + } + + fn collect_bang( + &mut self, + mac: ast::MacCall, + span: Span, + kind: AstFragmentKind, + ) -> AstFragment { + self.collect(kind, InvocationKind::Bang { mac, span }) + } + + fn collect_attr( + &mut self, + attr: Option<ast::Attribute>, + derives: Vec<Path>, + item: Annotatable, + kind: AstFragmentKind, + after_derive: bool, + ) -> AstFragment { + self.collect( + kind, + match attr { + Some(attr) => InvocationKind::Attr { attr, item, derives, after_derive }, + None => InvocationKind::DeriveContainer { derives, item }, + }, + ) + } + + fn find_attr_invoc( + &self, + attrs: &mut Vec<ast::Attribute>, + after_derive: &mut bool, + ) -> Option<ast::Attribute> { + let attr = attrs + .iter() + .position(|a| { + if a.has_name(sym::derive) { + *after_derive = true; + } + !self.cx.sess.is_attr_known(a) && !is_builtin_attr(a) + }) + .map(|i| attrs.remove(i)); + if let Some(attr) = &attr { + if !self.cx.ecfg.custom_inner_attributes() + && attr.style == ast::AttrStyle::Inner + && !attr.has_name(sym::test) + { + feature_err( + &self.cx.sess.parse_sess, + sym::custom_inner_attributes, + attr.span, + "non-builtin inner attributes are unstable", + ) + .emit(); + } + } + attr + } + + /// If `item` is an attr invocation, remove and return the macro attribute and derive traits. + fn classify_item( + &mut self, + item: &mut impl HasAttrs, + ) -> (Option<ast::Attribute>, Vec<Path>, /* after_derive */ bool) { + let (mut attr, mut traits, mut after_derive) = (None, Vec::new(), false); + + item.visit_attrs(|mut attrs| { + attr = self.find_attr_invoc(&mut attrs, &mut after_derive); + traits = collect_derives(&mut self.cx, &mut attrs); + }); + + (attr, traits, after_derive) + } + + /// Alternative to `classify_item()` that ignores `#[derive]` so invocations fallthrough + /// to the unused-attributes lint (making it an error on statements and expressions + /// is a breaking change) + fn classify_nonitem( + &mut self, + nonitem: &mut impl HasAttrs, + ) -> (Option<ast::Attribute>, /* after_derive */ bool) { + let (mut attr, mut after_derive) = (None, false); + + nonitem.visit_attrs(|mut attrs| { + attr = self.find_attr_invoc(&mut attrs, &mut after_derive); + }); + + (attr, after_derive) + } + + fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> { + self.cfg.configure(node) + } + + // Detect use of feature-gated or invalid attributes on macro invocations + // since they will not be detected after macro expansion. + fn check_attributes(&mut self, attrs: &[ast::Attribute]) { + let features = self.cx.ecfg.features.unwrap(); + for attr in attrs.iter() { + rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features); + validate_attr::check_meta(&self.cx.sess.parse_sess, attr); + + // macros are expanded before any lint passes so this warning has to be hardcoded + if attr.has_name(sym::derive) { + self.cx + .parse_sess() + .span_diagnostic + .struct_span_warn(attr.span, "`#[derive]` does nothing on macro invocations") + .note("this may become a hard error in a future release") + .emit(); + } + + if attr.doc_str().is_some() { + self.cx.sess.parse_sess.buffer_lint_with_diagnostic( + &UNUSED_DOC_COMMENTS, + attr.span, + ast::CRATE_NODE_ID, + "unused doc comment", + BuiltinLintDiagnostics::UnusedDocComment(attr.span), + ); + } + } + } +} + +impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { + fn visit_expr(&mut self, expr: &mut P<ast::Expr>) { + self.cfg.configure_expr(expr); + visit_clobber(expr.deref_mut(), |mut expr| { + self.cfg.configure_expr_kind(&mut expr.kind); + + // ignore derives so they remain unused + let (attr, after_derive) = self.classify_nonitem(&mut expr); + + if let Some(ref attr_value) = attr { + // Collect the invoc regardless of whether or not attributes are permitted here + // expansion will eat the attribute so it won't error later. + self.cfg.maybe_emit_expr_attr_err(attr_value); + + // AstFragmentKind::Expr requires the macro to emit an expression. + return self + .collect_attr( + attr, + vec![], + Annotatable::Expr(P(expr)), + AstFragmentKind::Expr, + after_derive, + ) + .make_expr() + .into_inner(); + } + + if let ast::ExprKind::MacCall(mac) = expr.kind { + self.check_attributes(&expr.attrs); + self.collect_bang(mac, expr.span, AstFragmentKind::Expr).make_expr().into_inner() + } else { + ensure_sufficient_stack(|| noop_visit_expr(&mut expr, self)); + expr + } + }); + } + + fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> { + let mut arm = configure!(self, arm); + + let (attr, traits, after_derive) = self.classify_item(&mut arm); + if attr.is_some() || !traits.is_empty() { + return self + .collect_attr( + attr, + traits, + Annotatable::Arm(arm), + AstFragmentKind::Arms, + after_derive, + ) + .make_arms(); + } + + noop_flat_map_arm(arm, self) + } + + fn flat_map_field(&mut self, field: ast::Field) -> SmallVec<[ast::Field; 1]> { + let mut field = configure!(self, field); + + let (attr, traits, after_derive) = self.classify_item(&mut field); + if attr.is_some() || !traits.is_empty() { + return self + .collect_attr( + attr, + traits, + Annotatable::Field(field), + AstFragmentKind::Fields, + after_derive, + ) + .make_fields(); + } + + noop_flat_map_field(field, self) + } + + fn flat_map_field_pattern(&mut self, fp: ast::FieldPat) -> SmallVec<[ast::FieldPat; 1]> { + let mut fp = configure!(self, fp); + + let (attr, traits, after_derive) = self.classify_item(&mut fp); + if attr.is_some() || !traits.is_empty() { + return self + .collect_attr( + attr, + traits, + Annotatable::FieldPat(fp), + AstFragmentKind::FieldPats, + after_derive, + ) + .make_field_patterns(); + } + + noop_flat_map_field_pattern(fp, self) + } + + fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> { + let mut p = configure!(self, p); + + let (attr, traits, after_derive) = self.classify_item(&mut p); + if attr.is_some() || !traits.is_empty() { + return self + .collect_attr( + attr, + traits, + Annotatable::Param(p), + AstFragmentKind::Params, + after_derive, + ) + .make_params(); + } + + noop_flat_map_param(p, self) + } + + fn flat_map_struct_field(&mut self, sf: ast::StructField) -> SmallVec<[ast::StructField; 1]> { + let mut sf = configure!(self, sf); + + let (attr, traits, after_derive) = self.classify_item(&mut sf); + if attr.is_some() || !traits.is_empty() { + return self + .collect_attr( + attr, + traits, + Annotatable::StructField(sf), + AstFragmentKind::StructFields, + after_derive, + ) + .make_struct_fields(); + } + + noop_flat_map_struct_field(sf, self) + } + + fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> { + let mut variant = configure!(self, variant); + + let (attr, traits, after_derive) = self.classify_item(&mut variant); + if attr.is_some() || !traits.is_empty() { + return self + .collect_attr( + attr, + traits, + Annotatable::Variant(variant), + AstFragmentKind::Variants, + after_derive, + ) + .make_variants(); + } + + noop_flat_map_variant(variant, self) + } + + fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> { + let expr = configure!(self, expr); + expr.filter_map(|mut expr| { + self.cfg.configure_expr_kind(&mut expr.kind); + + // Ignore derives so they remain unused. + let (attr, after_derive) = self.classify_nonitem(&mut expr); + + if let Some(ref attr_value) = attr { + self.cfg.maybe_emit_expr_attr_err(attr_value); + + return self + .collect_attr( + attr, + vec![], + Annotatable::Expr(P(expr)), + AstFragmentKind::OptExpr, + after_derive, + ) + .make_opt_expr() + .map(|expr| expr.into_inner()); + } + + if let ast::ExprKind::MacCall(mac) = expr.kind { + self.check_attributes(&expr.attrs); + self.collect_bang(mac, expr.span, AstFragmentKind::OptExpr) + .make_opt_expr() + .map(|expr| expr.into_inner()) + } else { + Some({ + noop_visit_expr(&mut expr, self); + expr + }) + } + }) + } + + fn visit_pat(&mut self, pat: &mut P<ast::Pat>) { + self.cfg.configure_pat(pat); + match pat.kind { + PatKind::MacCall(_) => {} + _ => return noop_visit_pat(pat, self), + } + + visit_clobber(pat, |mut pat| match mem::replace(&mut pat.kind, PatKind::Wild) { + PatKind::MacCall(mac) => { + self.collect_bang(mac, pat.span, AstFragmentKind::Pat).make_pat() + } + _ => unreachable!(), + }); + } + + fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { + let mut stmt = configure!(self, stmt); + + // we'll expand attributes on expressions separately + if !stmt.is_expr() { + let (attr, derives, after_derive) = if stmt.is_item() { + self.classify_item(&mut stmt) + } else { + // ignore derives on non-item statements so it falls through + // to the unused-attributes lint + let (attr, after_derive) = self.classify_nonitem(&mut stmt); + (attr, vec![], after_derive) + }; + + if attr.is_some() || !derives.is_empty() { + return self + .collect_attr( + attr, + derives, + Annotatable::Stmt(P(stmt)), + AstFragmentKind::Stmts, + after_derive, + ) + .make_stmts(); + } + } + + if let StmtKind::MacCall(mac) = stmt.kind { + let (mac, style, attrs) = mac.into_inner(); + self.check_attributes(&attrs); + let mut placeholder = + self.collect_bang(mac, stmt.span, AstFragmentKind::Stmts).make_stmts(); + + // If this is a macro invocation with a semicolon, then apply that + // semicolon to the final statement produced by expansion. + if style == MacStmtStyle::Semicolon { + if let Some(stmt) = placeholder.pop() { + placeholder.push(stmt.add_trailing_semicolon()); + } + } + + return placeholder; + } + + // The placeholder expander gives ids to statements, so we avoid folding the id here. + let ast::Stmt { id, kind, span } = stmt; + noop_flat_map_stmt_kind(kind, self) + .into_iter() + .map(|kind| ast::Stmt { id, kind, span }) + .collect() + } + + fn visit_block(&mut self, block: &mut P<Block>) { + let old_directory_ownership = self.cx.current_expansion.directory_ownership; + self.cx.current_expansion.directory_ownership = DirectoryOwnership::UnownedViaBlock; + noop_visit_block(block, self); + self.cx.current_expansion.directory_ownership = old_directory_ownership; + } + + fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> { + let mut item = configure!(self, item); + + let (attr, traits, after_derive) = self.classify_item(&mut item); + if attr.is_some() || !traits.is_empty() { + return self + .collect_attr( + attr, + traits, + Annotatable::Item(item), + AstFragmentKind::Items, + after_derive, + ) + .make_items(); + } + + let mut attrs = mem::take(&mut item.attrs); // We do this to please borrowck. + let ident = item.ident; + let span = item.span; + + match item.kind { + ast::ItemKind::MacCall(..) => { + item.attrs = attrs; + self.check_attributes(&item.attrs); + item.and_then(|item| match item.kind { + ItemKind::MacCall(mac) => self + .collect(AstFragmentKind::Items, InvocationKind::Bang { mac, span }) + .make_items(), + _ => unreachable!(), + }) + } + ast::ItemKind::Mod(ref mut old_mod @ ast::Mod { .. }) if ident != Ident::invalid() => { + let sess = &self.cx.sess.parse_sess; + let orig_ownership = self.cx.current_expansion.directory_ownership; + let mut module = (*self.cx.current_expansion.module).clone(); + + let pushed = &mut false; // Record `parse_external_mod` pushing so we can pop. + let dir = Directory { ownership: orig_ownership, path: module.directory }; + let Directory { ownership, path } = if old_mod.inline { + // Inline `mod foo { ... }`, but we still need to push directories. + item.attrs = attrs; + push_directory(&self.cx.sess, ident, &item.attrs, dir) + } else { + // We have an outline `mod foo;` so we need to parse the file. + let (new_mod, dir) = + parse_external_mod(&self.cx.sess, ident, span, dir, &mut attrs, pushed); + + let krate = ast::Crate { + span: new_mod.inner, + module: new_mod, + attrs, + proc_macros: vec![], + }; + if let Some(extern_mod_loaded) = self.cx.extern_mod_loaded { + extern_mod_loaded(&krate); + } + + *old_mod = krate.module; + item.attrs = krate.attrs; + // File can have inline attributes, e.g., `#![cfg(...)]` & co. => Reconfigure. + item = match self.configure(item) { + Some(node) => node, + None => { + if *pushed { + sess.included_mod_stack.borrow_mut().pop(); + } + return Default::default(); + } + }; + dir + }; + + // Set the module info before we flat map. + self.cx.current_expansion.directory_ownership = ownership; + module.directory = path; + module.mod_path.push(ident); + let orig_module = + mem::replace(&mut self.cx.current_expansion.module, Rc::new(module)); + + let result = noop_flat_map_item(item, self); + + // Restore the module info. + self.cx.current_expansion.module = orig_module; + self.cx.current_expansion.directory_ownership = orig_ownership; + if *pushed { + sess.included_mod_stack.borrow_mut().pop(); + } + result + } + _ => { + item.attrs = attrs; + noop_flat_map_item(item, self) + } + } + } + + fn flat_map_trait_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> { + let mut item = configure!(self, item); + + let (attr, traits, after_derive) = self.classify_item(&mut item); + if attr.is_some() || !traits.is_empty() { + return self + .collect_attr( + attr, + traits, + Annotatable::TraitItem(item), + AstFragmentKind::TraitItems, + after_derive, + ) + .make_trait_items(); + } + + match item.kind { + ast::AssocItemKind::MacCall(..) => { + self.check_attributes(&item.attrs); + item.and_then(|item| match item.kind { + ast::AssocItemKind::MacCall(mac) => self + .collect_bang(mac, item.span, AstFragmentKind::TraitItems) + .make_trait_items(), + _ => unreachable!(), + }) + } + _ => noop_flat_map_assoc_item(item, self), + } + } + + fn flat_map_impl_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> { + let mut item = configure!(self, item); + + let (attr, traits, after_derive) = self.classify_item(&mut item); + if attr.is_some() || !traits.is_empty() { + return self + .collect_attr( + attr, + traits, + Annotatable::ImplItem(item), + AstFragmentKind::ImplItems, + after_derive, + ) + .make_impl_items(); + } + + match item.kind { + ast::AssocItemKind::MacCall(..) => { + self.check_attributes(&item.attrs); + item.and_then(|item| match item.kind { + ast::AssocItemKind::MacCall(mac) => self + .collect_bang(mac, item.span, AstFragmentKind::ImplItems) + .make_impl_items(), + _ => unreachable!(), + }) + } + _ => noop_flat_map_assoc_item(item, self), + } + } + + fn visit_ty(&mut self, ty: &mut P<ast::Ty>) { + match ty.kind { + ast::TyKind::MacCall(_) => {} + _ => return noop_visit_ty(ty, self), + }; + + visit_clobber(ty, |mut ty| match mem::replace(&mut ty.kind, ast::TyKind::Err) { + ast::TyKind::MacCall(mac) => { + self.collect_bang(mac, ty.span, AstFragmentKind::Ty).make_ty() + } + _ => unreachable!(), + }); + } + + fn visit_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { + self.cfg.configure_foreign_mod(foreign_mod); + noop_visit_foreign_mod(foreign_mod, self); + } + + fn flat_map_foreign_item( + &mut self, + mut foreign_item: P<ast::ForeignItem>, + ) -> SmallVec<[P<ast::ForeignItem>; 1]> { + let (attr, traits, after_derive) = self.classify_item(&mut foreign_item); + + if attr.is_some() || !traits.is_empty() { + return self + .collect_attr( + attr, + traits, + Annotatable::ForeignItem(foreign_item), + AstFragmentKind::ForeignItems, + after_derive, + ) + .make_foreign_items(); + } + + match foreign_item.kind { + ast::ForeignItemKind::MacCall(..) => { + self.check_attributes(&foreign_item.attrs); + foreign_item.and_then(|item| match item.kind { + ast::ForeignItemKind::MacCall(mac) => self + .collect_bang(mac, item.span, AstFragmentKind::ForeignItems) + .make_foreign_items(), + _ => unreachable!(), + }) + } + _ => noop_flat_map_foreign_item(foreign_item, self), + } + } + + fn visit_item_kind(&mut self, item: &mut ast::ItemKind) { + match item { + ast::ItemKind::MacroDef(..) => {} + _ => { + self.cfg.configure_item_kind(item); + noop_visit_item_kind(item, self); + } + } + } + + fn flat_map_generic_param( + &mut self, + param: ast::GenericParam, + ) -> SmallVec<[ast::GenericParam; 1]> { + let mut param = configure!(self, param); + + let (attr, traits, after_derive) = self.classify_item(&mut param); + if attr.is_some() || !traits.is_empty() { + return self + .collect_attr( + attr, + traits, + Annotatable::GenericParam(param), + AstFragmentKind::GenericParams, + after_derive, + ) + .make_generic_params(); + } + + noop_flat_map_generic_param(param, self) + } + + fn visit_attribute(&mut self, at: &mut ast::Attribute) { + // turn `#[doc(include="filename")]` attributes into `#[doc(include(file="filename", + // contents="file contents")]` attributes + if !self.cx.sess.check_name(at, sym::doc) { + return noop_visit_attribute(at, self); + } + + if let Some(list) = at.meta_item_list() { + if !list.iter().any(|it| it.has_name(sym::include)) { + return noop_visit_attribute(at, self); + } + + let mut items = vec![]; + + for mut it in list { + if !it.has_name(sym::include) { + items.push({ + noop_visit_meta_list_item(&mut it, self); + it + }); + continue; + } + + if let Some(file) = it.value_str() { + let err_count = self.cx.sess.parse_sess.span_diagnostic.err_count(); + self.check_attributes(slice::from_ref(at)); + if self.cx.sess.parse_sess.span_diagnostic.err_count() > err_count { + // avoid loading the file if they haven't enabled the feature + return noop_visit_attribute(at, self); + } + + let filename = match self.cx.resolve_path(&*file.as_str(), it.span()) { + Ok(filename) => filename, + Err(mut err) => { + err.emit(); + continue; + } + }; + + match self.cx.source_map().load_file(&filename) { + Ok(source_file) => { + let src = source_file + .src + .as_ref() + .expect("freshly loaded file should have a source"); + let src_interned = Symbol::intern(src.as_str()); + + let include_info = vec![ + ast::NestedMetaItem::MetaItem(attr::mk_name_value_item_str( + Ident::with_dummy_span(sym::file), + file, + DUMMY_SP, + )), + ast::NestedMetaItem::MetaItem(attr::mk_name_value_item_str( + Ident::with_dummy_span(sym::contents), + src_interned, + DUMMY_SP, + )), + ]; + + let include_ident = Ident::with_dummy_span(sym::include); + let item = attr::mk_list_item(include_ident, include_info); + items.push(ast::NestedMetaItem::MetaItem(item)); + } + Err(e) => { + let lit = + it.meta_item().and_then(|item| item.name_value_literal()).unwrap(); + + if e.kind() == ErrorKind::InvalidData { + self.cx + .struct_span_err( + lit.span, + &format!("{} wasn't a utf-8 file", filename.display()), + ) + .span_label(lit.span, "contains invalid utf-8") + .emit(); + } else { + let mut err = self.cx.struct_span_err( + lit.span, + &format!("couldn't read {}: {}", filename.display(), e), + ); + err.span_label(lit.span, "couldn't read file"); + + err.emit(); + } + } + } + } else { + let mut err = self + .cx + .struct_span_err(it.span(), "expected path to external documentation"); + + // Check if the user erroneously used `doc(include(...))` syntax. + let literal = it.meta_item_list().and_then(|list| { + if list.len() == 1 { + list[0].literal().map(|literal| &literal.kind) + } else { + None + } + }); + + let (path, applicability) = match &literal { + Some(LitKind::Str(path, ..)) => { + (path.to_string(), Applicability::MachineApplicable) + } + _ => (String::from("<path>"), Applicability::HasPlaceholders), + }; + + err.span_suggestion( + it.span(), + "provide a file path with `=`", + format!("include = \"{}\"", path), + applicability, + ); + + err.emit(); + } + } + + let meta = attr::mk_list_item(Ident::with_dummy_span(sym::doc), items); + *at = ast::Attribute { + kind: ast::AttrKind::Normal(AttrItem { + path: meta.path, + args: meta.kind.mac_args(meta.span), + }), + span: at.span, + id: at.id, + style: at.style, + }; + } else { + noop_visit_attribute(at, self) + } + } + + fn visit_id(&mut self, id: &mut ast::NodeId) { + if self.monotonic { + debug_assert_eq!(*id, ast::DUMMY_NODE_ID); + *id = self.cx.resolver.next_node_id() + } + } + + fn visit_fn_decl(&mut self, mut fn_decl: &mut P<ast::FnDecl>) { + self.cfg.configure_fn_decl(&mut fn_decl); + noop_visit_fn_decl(fn_decl, self); + } +} + +pub struct ExpansionConfig<'feat> { + pub crate_name: String, + pub features: Option<&'feat Features>, + pub recursion_limit: Limit, + pub trace_mac: bool, + pub should_test: bool, // If false, strip `#[test]` nodes + pub keep_macs: bool, + pub span_debug: bool, // If true, use verbose debugging for `proc_macro::Span` +} + +impl<'feat> ExpansionConfig<'feat> { + pub fn default(crate_name: String) -> ExpansionConfig<'static> { + ExpansionConfig { + crate_name, + features: None, + recursion_limit: Limit::new(1024), + trace_mac: false, + should_test: false, + keep_macs: false, + span_debug: false, + } + } + + fn proc_macro_hygiene(&self) -> bool { + self.features.map_or(false, |features| features.proc_macro_hygiene) + } + fn custom_inner_attributes(&self) -> bool { + self.features.map_or(false, |features| features.custom_inner_attributes) + } +} |
