| use crate::base::*; |
| use crate::config::StripUnconfigured; |
| use crate::configure; |
| use crate::hygiene::SyntaxContext; |
| use crate::mbe::macro_rules::annotate_err_with_kind; |
| use crate::module::{mod_dir_path, parse_external_mod, DirOwnership, ParsedExternalMod}; |
| use crate::placeholders::{placeholder, PlaceholderExpander}; |
| |
| use rustc_ast as ast; |
| 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::{AstLike, AttrItem, Block, Inline, ItemKind, LitKind, MacArgs}; |
| use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem}; |
| use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe}; |
| use rustc_ast_pretty::pprust; |
| use rustc_attr::{self as attr, is_builtin_attr}; |
| use rustc_data_structures::map_in_place::MapInPlace; |
| use rustc_data_structures::stack::ensure_sufficient_stack; |
| use rustc_data_structures::sync::Lrc; |
| use rustc_errors::{Applicability, FatalError, PResult}; |
| use rustc_feature::Features; |
| use rustc_parse::parser::{AttemptLocalParseRecovery, ForceCollect, Parser, RecoverComma}; |
| 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::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::ExprField; 1]>) { |
| "field expression"; many fn flat_map_expr_field; fn visit_expr_field(); fn make_expr_fields; |
| } |
| FieldPats(SmallVec<[ast::PatField; 1]>) { |
| "field pattern"; |
| many fn flat_map_pat_field; |
| fn visit_pat_field(); |
| fn make_pat_fields; |
| } |
| 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::FieldDef; 1]>) { |
| "field"; |
| many fn flat_map_field_def; |
| fn visit_field_def(); |
| fn make_field_defs; |
| } |
| Variants(SmallVec<[ast::Variant; 1]>) { |
| "variant"; many fn flat_map_variant; fn visit_variant(); fn make_variants; |
| } |
| } |
| |
| pub enum SupportsMacroExpansion { |
| No, |
| Yes { supports_inner_attrs: bool }, |
| } |
| |
| impl AstFragmentKind { |
| crate fn dummy(self, span: Span) -> AstFragment { |
| self.make_from(DummyResult::any(span)).expect("couldn't create a dummy AST fragment") |
| } |
| |
| pub fn supports_macro_expansion(self) -> SupportsMacroExpansion { |
| match self { |
| AstFragmentKind::OptExpr |
| | AstFragmentKind::Expr |
| | AstFragmentKind::Stmts |
| | AstFragmentKind::Ty |
| | AstFragmentKind::Pat => SupportsMacroExpansion::Yes { supports_inner_attrs: false }, |
| AstFragmentKind::Items |
| | AstFragmentKind::TraitItems |
| | AstFragmentKind::ImplItems |
| | AstFragmentKind::ForeignItems => { |
| SupportsMacroExpansion::Yes { supports_inner_attrs: true } |
| } |
| AstFragmentKind::Arms |
| | AstFragmentKind::Fields |
| | AstFragmentKind::FieldPats |
| | AstFragmentKind::GenericParams |
| | AstFragmentKind::Params |
| | AstFragmentKind::StructFields |
| | AstFragmentKind::Variants => SupportsMacroExpansion::No, |
| } |
| } |
| |
| 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_expr_field).collect()) |
| } |
| AstFragmentKind::FieldPats => { |
| AstFragment::FieldPats(items.map(Annotatable::expect_pat_field).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_field_def).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, |
| // Re-insertion position for inert attributes. |
| pos: usize, |
| item: Annotatable, |
| // Required for resolving derive helper attributes. |
| derives: Vec<Path>, |
| }, |
| Derive { |
| path: 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::FieldDef(field), .. } |
| | InvocationKind::Derive { item: Annotatable::FieldDef(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, |
| } |
| } |
| } |
| |
| 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 } |
| } |
| |
| // FIXME: Avoid visiting the crate as a `Mod` item, |
| // make crate a first class expansion target instead. |
| pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate { |
| let file_path = match self.cx.source_map().span_to_unmapped_path(krate.span) { |
| FileName::Real(name) => name.into_local_path(), |
| other => PathBuf::from(other.to_string()), |
| }; |
| let dir_path = file_path.parent().unwrap_or(&file_path).to_owned(); |
| self.cx.root_path = dir_path.clone(); |
| self.cx.current_expansion.module = Rc::new(ModuleData { |
| mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)], |
| file_path_stack: vec![file_path], |
| dir_path, |
| }); |
| |
| let krate_item = AstFragment::Items(smallvec![P(ast::Item { |
| attrs: krate.attrs, |
| span: krate.span, |
| kind: ast::ItemKind::Mod( |
| Unsafe::No, |
| ModKind::Loaded(krate.items, Inline::Yes, krate.span) |
| ), |
| ident: Ident::invalid(), |
| id: ast::DUMMY_NODE_ID, |
| vis: ast::Visibility { |
| span: krate.span.shrink_to_lo(), |
| kind: ast::VisibilityKind::Public, |
| tokens: None, |
| }, |
| tokens: None, |
| })]); |
| |
| match self.fully_expand_fragment(krate_item).make_items().pop().map(P::into_inner) { |
| Some(ast::Item { |
| attrs, |
| kind: ast::ItemKind::Mod(_, ModKind::Loaded(items, ..)), |
| .. |
| }) => { |
| krate.attrs = attrs; |
| krate.items = items; |
| } |
| None => { |
| // Resolution failed so we return an empty expansion |
| krate.attrs = vec![]; |
| krate.items = vec![]; |
| } |
| Some(ast::Item { span, kind, .. }) => { |
| krate.attrs = vec![]; |
| krate.items = vec![]; |
| self.cx.span_err( |
| span, |
| &format!( |
| "expected crate top-level item to be a module after macro expansion, found {} {}", |
| kind.article(), kind.descr() |
| ), |
| ); |
| // FIXME: this workaround issue #84569 |
| FatalError.raise(); |
| } |
| }; |
| 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(); |
| let orig_force_mode = self.cx.force_mode; |
| 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, ext) = 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); |
| if force && self.monotonic { |
| self.cx.sess.delay_span_bug( |
| invocations.last().unwrap().0.span(), |
| "expansion entered force mode without producing any errors", |
| ); |
| } |
| continue; |
| }; |
| |
| let ext = match ext { |
| Some(ext) => ext, |
| 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(ext) => ext, |
| 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(); |
| self.cx.force_mode = force; |
| |
| let fragment_kind = invoc.fragment_kind; |
| let (expanded_fragment, new_invocations) = match self.expand_invoc(invoc, &ext.kind) { |
| ExpandResult::Ready(fragment) => { |
| let mut derive_invocations = Vec::new(); |
| let derive_placeholders = self |
| .cx |
| .resolver |
| .take_derive_resolutions(expn_id) |
| .map(|derives| { |
| enum AnnotatableRef<'a> { |
| Item(&'a P<ast::Item>), |
| Stmt(&'a ast::Stmt), |
| } |
| let item = match &fragment { |
| AstFragment::Items(items) => match &items[..] { |
| [item] => AnnotatableRef::Item(item), |
| _ => unreachable!(), |
| }, |
| AstFragment::Stmts(stmts) => match &stmts[..] { |
| [stmt] => AnnotatableRef::Stmt(stmt), |
| _ => unreachable!(), |
| }, |
| _ => unreachable!(), |
| }; |
| |
| derive_invocations.reserve(derives.len()); |
| derives |
| .into_iter() |
| .map(|(path, _exts)| { |
| // FIXME: Consider using the derive resolutions (`_exts`) |
| // instead of enqueuing the derives to be resolved again later. |
| let expn_id = ExpnId::fresh(None); |
| derive_invocations.push(( |
| Invocation { |
| kind: InvocationKind::Derive { |
| path, |
| item: match item { |
| AnnotatableRef::Item(item) => { |
| Annotatable::Item(item.clone()) |
| } |
| AnnotatableRef::Stmt(stmt) => { |
| Annotatable::Stmt(P(stmt.clone())) |
| } |
| }, |
| }, |
| fragment_kind, |
| expansion_data: ExpansionData { |
| id: expn_id, |
| ..self.cx.current_expansion.clone() |
| }, |
| }, |
| None, |
| )); |
| NodeId::placeholder_from_expn_id(expn_id) |
| }) |
| .collect::<Vec<_>>() |
| }) |
| .unwrap_or_default(); |
| |
| let (fragment, collected_invocations) = |
| self.collect_invocations(fragment, &derive_placeholders); |
| // We choose to expand any derive invocations associated with this macro invocation |
| // *before* any macro invocations collected from the output fragment |
| derive_invocations.extend(collected_invocations); |
| (fragment, derive_invocations) |
| } |
| ExpandResult::Retry(invoc) => { |
| if force { |
| self.cx.span_bug( |
| invoc.span(), |
| "expansion entered force mode but is still stuck", |
| ); |
| } else { |
| // Cannot expand, will retry this invocation later. |
| undetermined_invocations.push((invoc, Some(ext))); |
| continue; |
| } |
| } |
| }; |
| |
| 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; |
| self.cx.force_mode = orig_force_mode; |
| |
| // 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 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<Lrc<SyntaxExtension>>)>) { |
| // Resolve `$crate`s in the fragment for pretty-printing. |
| self.cx.resolver.resolve_dollar_crates(); |
| |
| let invocations = { |
| let mut collector = InvocationCollector { |
| // Non-derive macro invocations cannot see the results of cfg expansion - they |
| // will either be removed along with the item, or invoked before the cfg/cfg_attr |
| // attribute is expanded. Therefore, we don't need to configure the tokens |
| // Derive macros *can* see the results of cfg-expansion - they are handled |
| // specially in `fully_expand_fragment` |
| cfg: StripUnconfigured { |
| sess: &self.cx.sess, |
| features: self.cx.ecfg.features, |
| config_tokens: false, |
| }, |
| 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 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, pos, mut item, derives } => match ext { |
| SyntaxExtensionKind::Attr(expander) => { |
| self.gate_proc_macro_input(&item); |
| self.gate_proc_macro_attr_item(span, &item); |
| let mut fake_tokens = false; |
| if let Annotatable::Item(item_inner) = &item { |
| if let ItemKind::Mod(_, mod_kind) = &item_inner.kind { |
| // FIXME: Collect tokens and use them instead of generating |
| // fake ones. These are unstable, so it needs to be |
| // fixed prior to stabilization |
| // Fake tokens when we are invoking an inner attribute, and: |
| fake_tokens = matches!(attr.style, ast::AttrStyle::Inner) && |
| // We are invoking an attribute on the crate root, or an outline |
| // module |
| (item_inner.ident.name.is_empty() || !matches!(mod_kind, ast::ModKind::Loaded(_, Inline::Yes, _))); |
| } |
| } |
| let tokens = if fake_tokens { |
| rustc_parse::fake_token_stream( |
| &self.cx.sess.parse_sess, |
| &item.into_nonterminal(), |
| ) |
| } else { |
| 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) => { |
| // Reassemble the original invocation for retrying. |
| return ExpandResult::Retry(Invocation { |
| kind: InvocationKind::Attr { attr, pos, item, derives }, |
| ..invoc |
| }); |
| } |
| }; |
| if fragment_kind == AstFragmentKind::Expr && items.is_empty() { |
| let msg = |
| "removing an expression is not supported in this position"; |
| self.cx.span_err(span, msg); |
| fragment_kind.dummy(span) |
| } else { |
| 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.insert(pos, attr)); |
| fragment_kind.expect_from_annotatables(iter::once(item)) |
| } |
| _ => unreachable!(), |
| }, |
| InvocationKind::Derive { path, item } => match ext { |
| SyntaxExtensionKind::Derive(expander) |
| | SyntaxExtensionKind::LegacyDerive(expander) => { |
| 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) => { |
| // Reassemble the original invocation for retrying. |
| return ExpandResult::Retry(Invocation { |
| kind: InvocationKind::Derive { path: meta.path, item }, |
| ..invoc |
| }); |
| } |
| }; |
| fragment_kind.expect_from_annotatables(items) |
| } |
| _ => 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(stmt) => { |
| // Attributes are stable on item statements, |
| // but unstable on all other kinds of statements |
| if stmt.is_item() { |
| return; |
| } |
| "statements" |
| } |
| Annotatable::Expr(_) => "expressions", |
| Annotatable::Arm(..) |
| | Annotatable::ExprField(..) |
| | Annotatable::PatField(..) |
| | Annotatable::GenericParam(..) |
| | Annotatable::Param(..) |
| | Annotatable::FieldDef(..) |
| | 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(_, mod_kind) |
| if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) => |
| { |
| 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); |
| } |
| } |
| |
| 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) => { |
| if err.span.is_dummy() { |
| 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(ForceCollect::No)? { |
| items.push(item); |
| } |
| AstFragment::Items(items) |
| } |
| AstFragmentKind::TraitItems => { |
| let mut items = SmallVec::new(); |
| while let Some(item) = this.parse_trait_item(ForceCollect::No)? { |
| items.extend(item); |
| } |
| AstFragment::TraitItems(items) |
| } |
| AstFragmentKind::ImplItems => { |
| let mut items = SmallVec::new(); |
| while let Some(item) = this.parse_impl_item(ForceCollect::No)? { |
| items.extend(item); |
| } |
| AstFragment::ImplItems(items) |
| } |
| AstFragmentKind::ForeignItems => { |
| let mut items = SmallVec::new(); |
| while let Some(item) = this.parse_foreign_item(ForceCollect::No)? { |
| 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(AttemptLocalParseRecovery::Yes)? { |
| 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_allow_top_alt(None, RecoverComma::No)?) |
| } |
| 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<Lrc<SyntaxExtension>>)>, |
| monotonic: bool, |
| } |
| |
| impl<'a, 'b> InvocationCollector<'a, 'b> { |
| fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment { |
| let expn_id = ExpnId::fresh(None); |
| 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, pos, derives): (ast::Attribute, usize, Vec<Path>), |
| item: Annotatable, |
| kind: AstFragmentKind, |
| ) -> AstFragment { |
| self.collect(kind, InvocationKind::Attr { attr, pos, item, derives }) |
| } |
| |
| /// If `item` is an attribute invocation, remove the attribute and return it together with |
| /// its position and derives following it. We have to collect the derives in order to resolve |
| /// legacy derive helpers (helpers written before derives that introduce them). |
| fn take_first_attr( |
| &mut self, |
| item: &mut impl AstLike, |
| ) -> Option<(ast::Attribute, usize, Vec<Path>)> { |
| let mut attr = None; |
| |
| item.visit_attrs(|attrs| { |
| attr = attrs |
| .iter() |
| .position(|a| !self.cx.sess.is_attr_known(a) && !is_builtin_attr(a)) |
| .map(|attr_pos| { |
| let attr = attrs.remove(attr_pos); |
| let following_derives = attrs[attr_pos..] |
| .iter() |
| .filter(|a| a.has_name(sym::derive)) |
| .flat_map(|a| a.meta_item_list().unwrap_or_default()) |
| .filter_map(|nested_meta| match nested_meta { |
| NestedMetaItem::MetaItem(ast::MetaItem { |
| kind: MetaItemKind::Word, |
| path, |
| .. |
| }) => Some(path), |
| _ => None, |
| }) |
| .collect(); |
| |
| (attr, attr_pos, following_derives) |
| }) |
| }); |
| |
| attr |
| } |
| |
| fn configure<T: AstLike>(&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(); |
| let mut attrs = attrs.iter().peekable(); |
| let mut span: Option<Span> = None; |
| while let Some(attr) = attrs.next() { |
| rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features); |
| validate_attr::check_meta(&self.cx.sess.parse_sess, attr); |
| |
| let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; |
| span = Some(current_span); |
| |
| if attrs.peek().map_or(false, |next_attr| next_attr.doc_str().is_some()) { |
| continue; |
| } |
| |
| if attr.doc_str().is_some() { |
| self.cx.sess.parse_sess.buffer_lint_with_diagnostic( |
| &UNUSED_DOC_COMMENTS, |
| current_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| { |
| if let Some(attr) = self.take_first_attr(&mut expr) { |
| // 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.0); |
| |
| // AstFragmentKind::Expr requires the macro to emit an expression. |
| return self |
| .collect_attr(attr, Annotatable::Expr(P(expr)), AstFragmentKind::Expr) |
| .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); |
| |
| if let Some(attr) = self.take_first_attr(&mut arm) { |
| return self |
| .collect_attr(attr, Annotatable::Arm(arm), AstFragmentKind::Arms) |
| .make_arms(); |
| } |
| |
| noop_flat_map_arm(arm, self) |
| } |
| |
| fn flat_map_expr_field(&mut self, field: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> { |
| let mut field = configure!(self, field); |
| |
| if let Some(attr) = self.take_first_attr(&mut field) { |
| return self |
| .collect_attr(attr, Annotatable::ExprField(field), AstFragmentKind::Fields) |
| .make_expr_fields(); |
| } |
| |
| noop_flat_map_expr_field(field, self) |
| } |
| |
| fn flat_map_pat_field(&mut self, fp: ast::PatField) -> SmallVec<[ast::PatField; 1]> { |
| let mut fp = configure!(self, fp); |
| |
| if let Some(attr) = self.take_first_attr(&mut fp) { |
| return self |
| .collect_attr(attr, Annotatable::PatField(fp), AstFragmentKind::FieldPats) |
| .make_pat_fields(); |
| } |
| |
| noop_flat_map_pat_field(fp, self) |
| } |
| |
| fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> { |
| let mut p = configure!(self, p); |
| |
| if let Some(attr) = self.take_first_attr(&mut p) { |
| return self |
| .collect_attr(attr, Annotatable::Param(p), AstFragmentKind::Params) |
| .make_params(); |
| } |
| |
| noop_flat_map_param(p, self) |
| } |
| |
| fn flat_map_field_def(&mut self, sf: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> { |
| let mut sf = configure!(self, sf); |
| |
| if let Some(attr) = self.take_first_attr(&mut sf) { |
| return self |
| .collect_attr(attr, Annotatable::FieldDef(sf), AstFragmentKind::StructFields) |
| .make_field_defs(); |
| } |
| |
| noop_flat_map_field_def(sf, self) |
| } |
| |
| fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> { |
| let mut variant = configure!(self, variant); |
| |
| if let Some(attr) = self.take_first_attr(&mut variant) { |
| return self |
| .collect_attr(attr, Annotatable::Variant(variant), AstFragmentKind::Variants) |
| .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| { |
| if let Some(attr) = self.take_first_attr(&mut expr) { |
| self.cfg.maybe_emit_expr_attr_err(&attr.0); |
| |
| return self |
| .collect_attr(attr, Annotatable::Expr(P(expr)), AstFragmentKind::OptExpr) |
| .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>) { |
| 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() { |
| if let Some(attr) = self.take_first_attr(&mut stmt) { |
| return self |
| .collect_attr(attr, Annotatable::Stmt(P(stmt)), AstFragmentKind::Stmts) |
| .make_stmts(); |
| } |
| } |
| |
| if let StmtKind::MacCall(mac) = stmt.kind { |
| let MacCallStmt { mac, style, attrs, tokens: _ } = 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 orig_dir_ownership = mem::replace( |
| &mut self.cx.current_expansion.dir_ownership, |
| DirOwnership::UnownedViaBlock, |
| ); |
| noop_visit_block(block, self); |
| self.cx.current_expansion.dir_ownership = orig_dir_ownership; |
| } |
| |
| fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> { |
| let mut item = configure!(self, item); |
| |
| if let Some(attr) = self.take_first_attr(&mut item) { |
| return self |
| .collect_attr(attr, Annotatable::Item(item), AstFragmentKind::Items) |
| .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_bang(mac, span, AstFragmentKind::Items).make_items() |
| } |
| _ => unreachable!(), |
| }) |
| } |
| ast::ItemKind::Mod(_, ref mut mod_kind) if ident != Ident::invalid() => { |
| let (file_path, dir_path, dir_ownership) = match mod_kind { |
| ModKind::Loaded(_, inline, _) => { |
| // Inline `mod foo { ... }`, but we still need to push directories. |
| let (dir_path, dir_ownership) = mod_dir_path( |
| &self.cx.sess, |
| ident, |
| &attrs, |
| &self.cx.current_expansion.module, |
| self.cx.current_expansion.dir_ownership, |
| *inline, |
| ); |
| item.attrs = attrs; |
| (None, dir_path, dir_ownership) |
| } |
| ModKind::Unloaded => { |
| // We have an outline `mod foo;` so we need to parse the file. |
| let old_attrs_len = attrs.len(); |
| let ParsedExternalMod { |
| mut items, |
| inner_span, |
| file_path, |
| dir_path, |
| dir_ownership, |
| } = parse_external_mod( |
| &self.cx.sess, |
| ident, |
| span, |
| &self.cx.current_expansion.module, |
| self.cx.current_expansion.dir_ownership, |
| &mut attrs, |
| ); |
| |
| if let Some(extern_mod_loaded) = self.cx.extern_mod_loaded { |
| (attrs, items) = extern_mod_loaded(ident, attrs, items, inner_span); |
| } |
| |
| *mod_kind = ModKind::Loaded(items, Inline::No, inner_span); |
| item.attrs = attrs; |
| if item.attrs.len() > old_attrs_len { |
| // If we loaded an out-of-line module and added some inner attributes, |
| // then we need to re-configure it and re-collect attributes for |
| // resolution and expansion. |
| item = configure!(self, item); |
| |
| if let Some(attr) = self.take_first_attr(&mut item) { |
| return self |
| .collect_attr( |
| attr, |
| Annotatable::Item(item), |
| AstFragmentKind::Items, |
| ) |
| .make_items(); |
| } |
| } |
| (Some(file_path), dir_path, dir_ownership) |
| } |
| }; |
| |
| // Set the module info before we flat map. |
| let mut module = self.cx.current_expansion.module.with_dir_path(dir_path); |
| module.mod_path.push(ident); |
| if let Some(file_path) = file_path { |
| module.file_path_stack.push(file_path); |
| } |
| |
| let orig_module = |
| mem::replace(&mut self.cx.current_expansion.module, Rc::new(module)); |
| let orig_dir_ownership = |
| mem::replace(&mut self.cx.current_expansion.dir_ownership, dir_ownership); |
| |
| let result = noop_flat_map_item(item, self); |
| |
| // Restore the module info. |
| self.cx.current_expansion.dir_ownership = orig_dir_ownership; |
| self.cx.current_expansion.module = orig_module; |
| |
| 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); |
| |
| if let Some(attr) = self.take_first_attr(&mut item) { |
| return self |
| .collect_attr(attr, Annotatable::TraitItem(item), AstFragmentKind::TraitItems) |
| .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); |
| |
| if let Some(attr) = self.take_first_attr(&mut item) { |
| return self |
| .collect_attr(attr, Annotatable::ImplItem(item), AstFragmentKind::ImplItems) |
| .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 flat_map_foreign_item( |
| &mut self, |
| foreign_item: P<ast::ForeignItem>, |
| ) -> SmallVec<[P<ast::ForeignItem>; 1]> { |
| let mut foreign_item = configure!(self, foreign_item); |
| |
| if let Some(attr) = self.take_first_attr(&mut foreign_item) { |
| return self |
| .collect_attr( |
| attr, |
| Annotatable::ForeignItem(foreign_item), |
| AstFragmentKind::ForeignItems, |
| ) |
| .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 flat_map_generic_param( |
| &mut self, |
| param: ast::GenericParam, |
| ) -> SmallVec<[ast::GenericParam; 1]> { |
| let mut param = configure!(self, param); |
| |
| if let Some(attr) = self.take_first_attr(&mut param) { |
| return self |
| .collect_attr( |
| attr, |
| Annotatable::GenericParam(param), |
| AstFragmentKind::GenericParams, |
| ) |
| .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_span = it.name_value_literal_span().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), tokens: None }, |
| None, |
| ), |
| 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() |
| } |
| } |
| } |
| |
| 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 span_debug: bool, // If true, use verbose debugging for `proc_macro::Span` |
| pub proc_macro_backtrace: bool, // If true, show backtraces for proc-macro panics |
| } |
| |
| 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, |
| span_debug: false, |
| proc_macro_backtrace: false, |
| } |
| } |
| |
| fn proc_macro_hygiene(&self) -> bool { |
| self.features.map_or(false, |features| features.proc_macro_hygiene) |
| } |
| } |