| use std::path::PathBuf; | |
| use std::rc::Rc; | |
| use std::sync::Arc; | |
| use std::{iter, mem, slice}; | |
| use rustc_ast::mut_visit::*; | |
| use rustc_ast::tokenstream::TokenStream; | |
| use rustc_ast::visit::{AssocCtxt, Visitor, VisitorResult, try_visit, walk_list}; | |
| use rustc_ast::{ | |
| self as ast, AssocItemKind, AstNodeWrapper, AttrArgs, AttrKind, AttrStyle, AttrVec, | |
| DUMMY_NODE_ID, DelegationSource, DelegationSuffixes, ExprKind, ForeignItemKind, HasAttrs, | |
| HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind, NodeId, | |
| PatKind, StmtKind, SyntheticAttr, TyKind, token, | |
| }; | |
| use rustc_ast_pretty::pprust; | |
| use rustc_attr_parsing::parser::AllowExprMetavar; | |
| use rustc_attr_parsing::{ | |
| AttributeParser, AttributeSafety, CFG_TEMPLATE, EvalConfigResult, ShouldEmit, | |
| eval_config_entry, parse_cfg, validate_attr, | |
| }; | |
| use rustc_data_structures::Limit; | |
| use rustc_data_structures::flat_map_in_place::FlatMapInPlace; | |
| use rustc_data_structures::stack::ensure_sufficient_stack; | |
| use rustc_errors::PResult; | |
| use rustc_feature::Features; | |
| use rustc_hir::Target; | |
| use rustc_hir::def::MacroKinds; | |
| use rustc_parse::parser::{ | |
| AllowConstBlockItems, AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, | |
| RecoverColon, RecoverComma, Recovery, token_descr, | |
| }; | |
| use rustc_session::diagnostics::feature_err; | |
| use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; | |
| use rustc_span::hygiene::SyntaxContext; | |
| use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, Symbol, sym}; | |
| use smallvec::SmallVec; | |
| use crate::base::*; | |
| use crate::config::StripUnconfigured; | |
| use crate::diagnostics::{ | |
| EmptyDelegationMac, GlobDelegationOutsideImpls, GlobDelegationTraitlessQpath, IncompleteParse, | |
| RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue, | |
| WrongFragmentKind, | |
| }; | |
| use crate::mbe::diagnostics::annotate_err_with_kind; | |
| use crate::module::{ | |
| DirOwnership, ParsedExternalMod, mod_dir_path, mod_file_path_from_attr, parse_external_mod, | |
| }; | |
| use crate::placeholders::{PlaceholderExpander, placeholder}; | |
| use crate::stats::*; | |
| macro_rules! ast_fragments { | |
| ( | |
| $($Kind:ident($AstTy:ty) { | |
| $kind_name:expr; | |
| $(one | |
| 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<Box<ast::Expr>>), | |
| MethodReceiverExpr(Box<ast::Expr>), | |
| $($Kind($AstTy),)* | |
| } | |
| /// "Discriminant" of an AST fragment. | |
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] | |
| pub enum AstFragmentKind { | |
| OptExpr, | |
| MethodReceiverExpr, | |
| $($Kind,)* | |
| } | |
| impl AstFragmentKind { | |
| pub fn name(self) -> &'static str { | |
| match self { | |
| AstFragmentKind::OptExpr => "expression", | |
| AstFragmentKind::MethodReceiverExpr => "expression", | |
| $(AstFragmentKind::$Kind => $kind_name,)* | |
| } | |
| } | |
| fn make_from(self, result: Box<dyn MacResult + '_>) -> Option<AstFragment> { | |
| match self { | |
| AstFragmentKind::OptExpr => | |
| result.make_expr().map(Some).map(AstFragment::OptExpr), | |
| AstFragmentKind::MethodReceiverExpr => | |
| result.make_expr().map(AstFragment::MethodReceiverExpr), | |
| $(AstFragmentKind::$Kind => result.$make_ast().map(AstFragment::$Kind),)* | |
| } | |
| } | |
| } | |
| impl AstFragment { | |
| fn add_placeholders(&mut self, placeholders: &[NodeId]) { | |
| if placeholders.is_empty() { | |
| return; | |
| } | |
| match self { | |
| $($(AstFragment::$Kind(ast) => ast.extend(placeholders.iter().flat_map(|id| { | |
| ${ignore($flat_map_ast_elt)} | |
| placeholder(AstFragmentKind::$Kind, *id, None).$make_ast() | |
| })),)?)* | |
| _ => panic!("unexpected AST fragment kind") | |
| } | |
| } | |
| pub(crate) fn make_opt_expr(self) -> Option<Box<ast::Expr>> { | |
| match self { | |
| AstFragment::OptExpr(expr) => expr, | |
| _ => panic!("AstFragment::make_opt_expr called on the wrong kind of fragment"), | |
| } | |
| } | |
| pub(crate) fn make_method_receiver_expr(self) -> Box<ast::Expr> { | |
| match self { | |
| AstFragment::MethodReceiverExpr(expr) => expr, | |
| _ => panic!("AstFragment::make_method_receiver_expr called on the wrong kind of fragment"), | |
| } | |
| } | |
| $(pub fn $make_ast(self) -> $AstTy { | |
| match self { | |
| AstFragment::$Kind(ast) => ast, | |
| _ => panic!("AstFragment::{} called on the wrong kind of fragment", stringify!($make_ast)), | |
| } | |
| })* | |
| fn make_ast<T: InvocationCollectorNode>(self) -> T::OutputTy { | |
| T::fragment_to_output(self) | |
| } | |
| pub(crate) fn mut_visit_with(&mut self, vis: &mut impl MutVisitor) { | |
| match self { | |
| AstFragment::OptExpr(opt_expr) => { | |
| if let Some(expr) = opt_expr.take() { | |
| *opt_expr = vis.filter_map_expr(expr) | |
| } | |
| } | |
| AstFragment::MethodReceiverExpr(expr) => vis.visit_method_receiver_expr(expr), | |
| $($(AstFragment::$Kind(ast) => vis.$visit_ast(ast),)?)* | |
| $($(AstFragment::$Kind(ast) => | |
| ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast, $($args)*)),)?)* | |
| } | |
| } | |
| pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) -> V::Result { | |
| match self { | |
| AstFragment::OptExpr(Some(expr)) => try_visit!(visitor.visit_expr(expr)), | |
| AstFragment::OptExpr(None) => {} | |
| AstFragment::MethodReceiverExpr(expr) => try_visit!(visitor.visit_method_receiver_expr(expr)), | |
| $($(AstFragment::$Kind(ast) => try_visit!(visitor.$visit_ast(ast)),)?)* | |
| $($(AstFragment::$Kind(ast) => walk_list!(visitor, $visit_ast_elt, &ast[..], $($args)*),)?)* | |
| } | |
| V::Result::output() | |
| } | |
| } | |
| impl<'a, 'b> MacResult for crate::mbe::macro_rules::ParserAnyMacro<'a, 'b> { | |
| $(fn $make_ast(self: Box<crate::mbe::macro_rules::ParserAnyMacro<'a, 'b>>) | |
| -> Option<$AstTy> { | |
| Some(self.make(AstFragmentKind::$Kind).$make_ast()) | |
| })* | |
| } | |
| } | |
| } | |
| ast_fragments! { | |
| Expr(Box<ast::Expr>) { | |
| "expression"; | |
| one fn visit_expr; | |
| fn make_expr; | |
| } | |
| Pat(Box<ast::Pat>) { | |
| "pattern"; | |
| one fn visit_pat; | |
| fn make_pat; | |
| } | |
| Ty(Box<ast::Ty>) { | |
| "type"; | |
| one 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<[Box<ast::Item>; 1]>) { | |
| "item"; | |
| many fn flat_map_item; fn visit_item(); | |
| fn make_items; | |
| } | |
| TraitItems(SmallVec<[Box<ast::AssocItem>; 1]>) { | |
| "trait item"; | |
| many fn flat_map_assoc_item; fn visit_assoc_item(AssocCtxt::Trait); | |
| fn make_trait_items; | |
| } | |
| ImplItems(SmallVec<[Box<ast::AssocItem>; 1]>) { | |
| "impl item"; | |
| many fn flat_map_assoc_item; fn visit_assoc_item(AssocCtxt::Impl { of_trait: false }); | |
| fn make_impl_items; | |
| } | |
| TraitImplItems(SmallVec<[Box<ast::AssocItem>; 1]>) { | |
| "impl item"; | |
| many fn flat_map_assoc_item; fn visit_assoc_item(AssocCtxt::Impl { of_trait: true }); | |
| fn make_trait_impl_items; | |
| } | |
| ForeignItems(SmallVec<[Box<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; | |
| } | |
| ExprFields(SmallVec<[ast::ExprField; 1]>) { | |
| "field expression"; | |
| many fn flat_map_expr_field; fn visit_expr_field(); | |
| fn make_expr_fields; | |
| } | |
| PatFields(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; | |
| } | |
| FieldDefs(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; | |
| } | |
| WherePredicates(SmallVec<[ast::WherePredicate; 1]>) { | |
| "where predicate"; | |
| many fn flat_map_where_predicate; fn visit_where_predicate(); | |
| fn make_where_predicates; | |
| } | |
| Crate(ast::Crate) { | |
| "crate"; | |
| one fn visit_crate; | |
| fn make_crate; | |
| } | |
| } | |
| pub enum SupportsMacroExpansion { | |
| No, | |
| Yes { supports_inner_attrs: bool }, | |
| } | |
| impl AstFragmentKind { | |
| pub(crate) fn dummy(self, span: Span, guar: ErrorGuaranteed) -> AstFragment { | |
| self.make_from(DummyResult::any(span, guar)).expect("couldn't create a dummy AST fragment") | |
| } | |
| pub fn supports_macro_expansion(self) -> SupportsMacroExpansion { | |
| match self { | |
| AstFragmentKind::OptExpr | |
| | AstFragmentKind::Expr | |
| | AstFragmentKind::MethodReceiverExpr | |
| | AstFragmentKind::Stmts | |
| | AstFragmentKind::Ty | |
| | AstFragmentKind::Pat => SupportsMacroExpansion::Yes { supports_inner_attrs: false }, | |
| AstFragmentKind::Items | |
| | AstFragmentKind::TraitItems | |
| | AstFragmentKind::ImplItems | |
| | AstFragmentKind::TraitImplItems | |
| | AstFragmentKind::ForeignItems | |
| | AstFragmentKind::Crate => SupportsMacroExpansion::Yes { supports_inner_attrs: true }, | |
| AstFragmentKind::Arms | |
| | AstFragmentKind::ExprFields | |
| | AstFragmentKind::PatFields | |
| | AstFragmentKind::GenericParams | |
| | AstFragmentKind::Params | |
| | AstFragmentKind::FieldDefs | |
| | AstFragmentKind::Variants | |
| | AstFragmentKind::WherePredicates => SupportsMacroExpansion::No, | |
| } | |
| } | |
| pub(crate) fn expect_from_annotatables( | |
| self, | |
| items: impl IntoIterator<Item = Annotatable>, | |
| ) -> AstFragment { | |
| let mut items = items.into_iter(); | |
| match self { | |
| AstFragmentKind::Arms => { | |
| AstFragment::Arms(items.map(Annotatable::expect_arm).collect()) | |
| } | |
| AstFragmentKind::ExprFields => { | |
| AstFragment::ExprFields(items.map(Annotatable::expect_expr_field).collect()) | |
| } | |
| AstFragmentKind::PatFields => { | |
| AstFragment::PatFields(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::FieldDefs => { | |
| AstFragment::FieldDefs(items.map(Annotatable::expect_field_def).collect()) | |
| } | |
| AstFragmentKind::Variants => { | |
| AstFragment::Variants(items.map(Annotatable::expect_variant).collect()) | |
| } | |
| AstFragmentKind::WherePredicates => AstFragment::WherePredicates( | |
| items.map(Annotatable::expect_where_predicate).collect(), | |
| ), | |
| AstFragmentKind::Items => { | |
| AstFragment::Items(items.map(Annotatable::expect_item).collect()) | |
| } | |
| AstFragmentKind::ImplItems => { | |
| AstFragment::ImplItems(items.map(Annotatable::expect_impl_item).collect()) | |
| } | |
| AstFragmentKind::TraitImplItems => { | |
| AstFragment::TraitImplItems(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::MethodReceiverExpr => AstFragment::MethodReceiverExpr( | |
| items.next().expect("expected exactly one expression").expect_expr(), | |
| ), | |
| AstFragmentKind::OptExpr => { | |
| AstFragment::OptExpr(items.next().map(Annotatable::expect_expr)) | |
| } | |
| AstFragmentKind::Crate => { | |
| AstFragment::Crate(items.next().expect("expected exactly one crate").expect_crate()) | |
| } | |
| 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: Box<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<ast::Path>, | |
| }, | |
| Derive { | |
| path: ast::Path, | |
| is_const: bool, | |
| item: Annotatable, | |
| }, | |
| GlobDelegation { | |
| item: Box<ast::AssocItem>, | |
| /// Whether this is a trait impl or an inherent impl | |
| of_trait: bool, | |
| }, | |
| } | |
| 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 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, | |
| InvocationKind::GlobDelegation { item, .. } => item.span, | |
| } | |
| } | |
| fn span_mut(&mut self) -> &mut Span { | |
| match &mut self.kind { | |
| InvocationKind::Bang { span, .. } => span, | |
| InvocationKind::Attr { attr, .. } => &mut attr.span, | |
| InvocationKind::Derive { path, .. } => &mut path.span, | |
| InvocationKind::GlobDelegation { item, .. } => &mut 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, krate: ast::Crate) -> ast::Crate { | |
| let file_path = match self.cx.source_map().span_to_filename(krate.spans.inner_span) { | |
| FileName::Real(name) => name | |
| .into_local_path() | |
| .expect("attempting to resolve a file path in an external file"), | |
| other => PathBuf::from(other.prefer_local_unconditionally().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::with_dummy_span(self.cx.ecfg.crate_name)], | |
| file_path_stack: vec![file_path], | |
| dir_path, | |
| }); | |
| let krate = self.fully_expand_fragment(AstFragment::Crate(krate)).make_crate(); | |
| assert_eq!(krate.id, ast::CRATE_NODE_ID); | |
| 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; | |
| // 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 expanded_fragments_len = 0; | |
| let mut undetermined_invocations = Vec::new(); | |
| let (mut progress, mut force) = (false, !self.monotonic); | |
| loop { | |
| let Some((invoc, ext)) = invocations.pop() else { | |
| self.resolve_imports(); | |
| if undetermined_invocations.is_empty() { | |
| break; | |
| } | |
| invocations = mem::take(&mut undetermined_invocations); | |
| force = !progress; | |
| progress = false; | |
| if force && self.monotonic { | |
| self.cx.dcx().span_delayed_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; | |
| let depth = depth - orig_expansion_data.depth; | |
| self.cx.current_expansion = invoc.expansion_data.clone(); | |
| self.cx.force_mode = force; | |
| let fragment_kind = invoc.fragment_kind; | |
| 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| { | |
| derive_invocations.reserve(derives.len()); | |
| derives | |
| .into_iter() | |
| .map(|DeriveResolution { path, item, exts: _, is_const }| { | |
| // FIXME: Consider using the derive resolutions (`_exts`) | |
| // instead of enqueuing the derives to be resolved again later. | |
| // Note that this can result in duplicate diagnostics. | |
| let expn_id = LocalExpnId::fresh_empty(); | |
| derive_invocations.push(( | |
| Invocation { | |
| kind: InvocationKind::Derive { path, item, is_const }, | |
| 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 (expanded_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); | |
| progress = true; | |
| if expanded_fragments.len() < depth { | |
| expanded_fragments.push(Vec::new()); | |
| } | |
| expanded_fragments[depth - 1].push((expn_id, expanded_fragment)); | |
| expanded_fragments_len += 1; | |
| invocations.extend(derive_invocations.into_iter().rev()); | |
| } | |
| ExpandResult::Retry(invoc) => { | |
| if force { | |
| self.cx.dcx().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))); | |
| } | |
| } | |
| } | |
| } | |
| 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::with_capacity(expanded_fragments_len); | |
| 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<Arc<SyntaxExtension>>)>) { | |
| // Resolve `$crate`s in the fragment for pretty-printing. | |
| self.cx.resolver.resolve_dollar_crates(); | |
| let mut 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` | |
| 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); | |
| if self.cx.sess.opts.incremental.is_some() { | |
| for (invoc, _) in invocations.iter_mut() { | |
| let expn_id = invoc.expansion_data.id; | |
| let parent_def = self.cx.resolver.invocation_parent(expn_id); | |
| let span = invoc.span_mut(); | |
| *span = span.with_parent(Some(parent_def)); | |
| } | |
| } | |
| } | |
| (fragment, invocations) | |
| } | |
| fn error_recursion_limit_reached(&mut self) -> ErrorGuaranteed { | |
| let expn_data = self.cx.current_expansion.id.expn_data(); | |
| let suggested_limit = match self.cx.ecfg.recursion_limit { | |
| Limit(0) => Limit(2), | |
| limit => limit * 2, | |
| }; | |
| let guar = self.cx.dcx().emit_err(RecursionLimitReached { | |
| span: expn_data.call_site, | |
| descr: expn_data.kind.descr(), | |
| suggested_limit, | |
| crate_name: self.cx.ecfg.crate_name, | |
| }); | |
| self.cx.macro_error_and_trace_macros_diag(); | |
| guar | |
| } | |
| /// 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, | |
| ) -> ErrorGuaranteed { | |
| let name = pprust::path_to_string(&mac.path); | |
| let guar = self.cx.dcx().emit_err(WrongFragmentKind { span, kind: kind.name(), name }); | |
| self.cx.macro_error_and_trace_macros_diag(); | |
| guar | |
| } | |
| fn expand_invoc( | |
| &mut self, | |
| invoc: Invocation, | |
| ext: &SyntaxExtensionKind, | |
| ) -> ExpandResult<AstFragment, Invocation> { | |
| let recursion_limit = match self.cx.reduced_recursion_limit { | |
| Some((limit, _)) => limit, | |
| None => self.cx.ecfg.recursion_limit, | |
| }; | |
| if !recursion_limit.value_within_limit(self.cx.current_expansion.depth) { | |
| let guar = match self.cx.reduced_recursion_limit { | |
| Some((_, guar)) => guar, | |
| 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, guar)); | |
| return ExpandResult::Ready(invoc.fragment_kind.dummy(invoc.span(), guar)); | |
| } | |
| let macro_stats = self.cx.sess.opts.unstable_opts.macro_stats; | |
| let (fragment_kind, span) = (invoc.fragment_kind, invoc.span()); | |
| ExpandResult::Ready(match invoc.kind { | |
| InvocationKind::Bang { mac, span } => { | |
| if let SyntaxExtensionKind::Bang(expander) = ext { | |
| match expander.expand(self.cx, span, mac.args.tokens.clone()) { | |
| Ok(tok_result) => { | |
| let fragment = | |
| self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span); | |
| if macro_stats { | |
| update_bang_macro_stats( | |
| self.cx, | |
| fragment_kind, | |
| span, | |
| mac, | |
| &fragment, | |
| ); | |
| } | |
| fragment | |
| } | |
| Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)), | |
| } | |
| } else if let Some(expander) = ext.as_legacy_bang() { | |
| let tok_result = match expander.expand(self.cx, span, mac.args.tokens.clone()) { | |
| ExpandResult::Ready(tok_result) => tok_result, | |
| ExpandResult::Retry(_) => { | |
| // retry the original | |
| return ExpandResult::Retry(Invocation { | |
| kind: InvocationKind::Bang { mac, span }, | |
| ..invoc | |
| }); | |
| } | |
| }; | |
| if let Some(fragment) = fragment_kind.make_from(tok_result) { | |
| if macro_stats { | |
| update_bang_macro_stats(self.cx, fragment_kind, span, mac, &fragment); | |
| } | |
| fragment | |
| } else { | |
| let guar = self.error_wrong_fragment_kind(fragment_kind, &mac, span); | |
| fragment_kind.dummy(span, guar) | |
| } | |
| } else { | |
| unreachable!(); | |
| } | |
| } | |
| InvocationKind::Attr { attr, pos, mut item, derives } => { | |
| if let Some(expander) = ext.as_attr() { | |
| self.gate_proc_macro_attr_item(span, &item); | |
| let tokens = match &item { | |
| // 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 | |
| // we are invoking it on an out-of-line module or crate. | |
| Annotatable::Crate(krate) => { | |
| rustc_parse::fake_token_stream_for_crate(&self.cx.sess.psess, krate) | |
| } | |
| Annotatable::Item(item_inner) | |
| if matches!(attr.style, AttrStyle::Inner) | |
| && matches!( | |
| item_inner.kind, | |
| ItemKind::Mod( | |
| _, | |
| _, | |
| ModKind::Unloaded | |
| | ModKind::Loaded(_, Inline::No { .. }, _), | |
| ) | |
| ) => | |
| { | |
| rustc_parse::fake_token_stream_for_item( | |
| &self.cx.sess.psess, | |
| item_inner, | |
| Some(&attr), | |
| ) | |
| } | |
| Annotatable::Item(item_inner) if item_inner.tokens.is_none() => { | |
| rustc_parse::fake_token_stream_for_item( | |
| &self.cx.sess.psess, | |
| item_inner, | |
| None, | |
| ) | |
| } | |
| // When a function has EII implementations attached (via `eii_impl`), | |
| // use fake tokens so the pretty-printer re-emits the EII attribute | |
| // (e.g. `#[hello]`) in the token stream. Without this, the EII | |
| // attribute is lost during the token roundtrip performed by | |
| // `AttrProcMacro` expanders like `contracts::requires/ensures`, | |
| // breaking the EII link on the resulting re-parsed item. | |
| Annotatable::Item(item_inner) | |
| if matches!(&item_inner.kind, | |
| ItemKind::Fn(f) if f.eii_impl.is_some()) => | |
| { | |
| rustc_parse::fake_token_stream_for_item( | |
| &self.cx.sess.psess, | |
| item_inner, | |
| None, | |
| ) | |
| } | |
| Annotatable::ForeignItem(item_inner) if item_inner.tokens.is_none() => { | |
| rustc_parse::fake_token_stream_for_foreign_item( | |
| &self.cx.sess.psess, | |
| item_inner, | |
| ) | |
| } | |
| _ => item.to_tokens(), | |
| }; | |
| let attr_item = attr.get_normal_item(); | |
| let safety = attr_item.unsafety; | |
| if let AttrArgs::Eq { .. } = attr_item.args { | |
| self.cx.dcx().emit_err(UnsupportedKeyValue { span }); | |
| } | |
| let inner_tokens = attr_item.args.inner_tokens(); | |
| match expander.expand_with_safety(self.cx, safety, span, inner_tokens, tokens) { | |
| Ok(tok_result) => { | |
| let fragment = self.parse_ast_fragment( | |
| tok_result, | |
| fragment_kind, | |
| &attr_item.path, | |
| span, | |
| ); | |
| if macro_stats { | |
| update_attr_macro_stats( | |
| self.cx, | |
| fragment_kind, | |
| span, | |
| &attr_item.path, | |
| &attr, | |
| item, | |
| &fragment, | |
| ); | |
| } | |
| fragment | |
| } | |
| Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)), | |
| } | |
| } else if let SyntaxExtensionKind::LegacyAttr(expander) = ext { | |
| // `LegacyAttr` is only used for builtin attribute macros, which have their | |
| // safety checked by `check_builtin_meta_item`, so we don't need to check | |
| // `unsafety` here. | |
| match validate_attr::parse_meta(&self.cx.sess.psess, &attr) { | |
| Ok(meta) => { | |
| let item_clone = macro_stats.then(|| item.clone()); | |
| let items = match expander.expand(self.cx, span, &meta, item, false) { | |
| 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 matches!( | |
| fragment_kind, | |
| AstFragmentKind::Expr | AstFragmentKind::MethodReceiverExpr | |
| ) && items.is_empty() | |
| { | |
| let guar = self.cx.dcx().emit_err(RemoveExprNotSupported { span }); | |
| fragment_kind.dummy(span, guar) | |
| } else { | |
| let fragment = fragment_kind.expect_from_annotatables(items); | |
| if macro_stats { | |
| update_attr_macro_stats( | |
| self.cx, | |
| fragment_kind, | |
| span, | |
| &meta.path, | |
| &attr, | |
| item_clone.unwrap(), | |
| &fragment, | |
| ); | |
| } | |
| fragment | |
| } | |
| } | |
| Err(err) => { | |
| let _guar = err.emit(); | |
| fragment_kind.expect_from_annotatables(iter::once(item)) | |
| } | |
| } | |
| } else if let SyntaxExtensionKind::NonMacroAttr = ext { | |
| // `-Zmacro-stats` ignores these because they don't do any real expansion. | |
| self.cx.expanded_inert_attrs.mark(&attr); | |
| item.visit_attrs(|attrs| attrs.insert(pos, attr)); | |
| fragment_kind.expect_from_annotatables(iter::once(item)) | |
| } else { | |
| unreachable!(); | |
| } | |
| } | |
| InvocationKind::Derive { path, item, is_const } => match ext { | |
| SyntaxExtensionKind::Derive(expander) | |
| | SyntaxExtensionKind::LegacyDerive(expander) => { | |
| // The `MetaItem` representing the trait to derive can't | |
| // have an unsafe around it (as of now). | |
| let meta = ast::MetaItem { | |
| unsafety: ast::Safety::Default, | |
| kind: MetaItemKind::Word, | |
| span, | |
| path, | |
| }; | |
| let items = match expander.expand(self.cx, span, &meta, item, is_const) { | |
| ExpandResult::Ready(items) => items, | |
| ExpandResult::Retry(item) => { | |
| // Reassemble the original invocation for retrying. | |
| return ExpandResult::Retry(Invocation { | |
| kind: InvocationKind::Derive { path: meta.path, item, is_const }, | |
| ..invoc | |
| }); | |
| } | |
| }; | |
| let fragment = fragment_kind.expect_from_annotatables(items); | |
| if macro_stats { | |
| update_derive_macro_stats( | |
| self.cx, | |
| fragment_kind, | |
| span, | |
| &meta.path, | |
| &fragment, | |
| ); | |
| } | |
| fragment | |
| } | |
| SyntaxExtensionKind::MacroRules(expander) | |
| if expander.kinds().contains(MacroKinds::DERIVE) => | |
| { | |
| if is_const { | |
| let guar = self | |
| .cx | |
| .dcx() | |
| .span_err(span, "macro `derive` does not support const derives"); | |
| return ExpandResult::Ready(fragment_kind.dummy(span, guar)); | |
| } | |
| let body = item.to_tokens(); | |
| match expander.expand_derive(self.cx, span, &body) { | |
| Ok(tok_result) => { | |
| let fragment = | |
| self.parse_ast_fragment(tok_result, fragment_kind, &path, span); | |
| if macro_stats { | |
| update_derive_macro_stats( | |
| self.cx, | |
| fragment_kind, | |
| span, | |
| &path, | |
| &fragment, | |
| ); | |
| } | |
| fragment | |
| } | |
| Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)), | |
| } | |
| } | |
| _ => unreachable!(), | |
| }, | |
| InvocationKind::GlobDelegation { item, of_trait } => { | |
| let AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() }; | |
| let suffixes = match ext { | |
| SyntaxExtensionKind::GlobDelegation(expander) => match expander.expand(self.cx) | |
| { | |
| ExpandResult::Ready(suffixes) => suffixes, | |
| ExpandResult::Retry(()) => { | |
| // Reassemble the original invocation for retrying. | |
| return ExpandResult::Retry(Invocation { | |
| kind: InvocationKind::GlobDelegation { item, of_trait }, | |
| ..invoc | |
| }); | |
| } | |
| }, | |
| SyntaxExtensionKind::Bang(..) => { | |
| let msg = "expanded a dummy glob delegation"; | |
| let guar = self.cx.dcx().span_delayed_bug(span, msg); | |
| return ExpandResult::Ready(fragment_kind.dummy(span, guar)); | |
| } | |
| _ => unreachable!(), | |
| }; | |
| type Node = AstNodeWrapper<Box<ast::AssocItem>, ImplItemTag>; | |
| let single_delegations = build_single_delegations::<Node>( | |
| self.cx, | |
| deleg, | |
| &item, | |
| &suffixes, | |
| item.span, | |
| DelegationSource::Glob, | |
| ); | |
| // `-Zmacro-stats` ignores these because they don't seem important. | |
| fragment_kind.expect_from_annotatables(single_delegations.map(|item| { | |
| Annotatable::AssocItem(Box::new(item), AssocCtxt::Impl { of_trait }) | |
| })) | |
| } | |
| }) | |
| } | |
| fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) { | |
| let kind = match item { | |
| Annotatable::Item(_) | |
| | Annotatable::AssocItem(..) | |
| | Annotatable::ForeignItem(_) | |
| | Annotatable::Crate(..) => 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(..) | |
| | Annotatable::WherePredicate(..) => panic!("unexpected annotatable"), | |
| }; | |
| if self.cx.ecfg.features.proc_macro_hygiene() { | |
| return; | |
| } | |
| feature_err( | |
| self.cx.sess, | |
| sym::proc_macro_hygiene, | |
| span, | |
| format!("custom attributes cannot be applied to {kind}"), | |
| ) | |
| .emit(); | |
| } | |
| fn parse_ast_fragment( | |
| &mut self, | |
| toks: TokenStream, | |
| kind: AstFragmentKind, | |
| path: &ast::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(&parser, path, kind.name(), span); | |
| fragment | |
| } | |
| Err(mut err) => { | |
| if err.span.is_dummy() { | |
| err.span(span); | |
| } | |
| annotate_err_with_kind(&mut err, kind, span); | |
| let guar = err.emit(); | |
| self.cx.macro_error_and_trace_macros_diag(); | |
| kind.dummy(span, guar) | |
| } | |
| } | |
| } | |
| } | |
| 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, AllowConstBlockItems::Yes)? { | |
| 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::TraitImplItems => { | |
| let mut items = SmallVec::new(); | |
| while let Some(item) = this.parse_impl_item(ForceCollect::No)? { | |
| items.extend(item); | |
| } | |
| AstFragment::TraitImplItems(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::CloseBrace { | |
| stmts.push(this.parse_full_stmt(AttemptLocalParseRecovery::Yes)?); | |
| } | |
| AstFragment::Stmts(stmts) | |
| } | |
| AstFragmentKind::Expr => AstFragment::Expr(this.parse_expr()?), | |
| AstFragmentKind::MethodReceiverExpr => AstFragment::MethodReceiverExpr(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(Box::new(this.parse_pat_allow_top_guard( | |
| None, | |
| RecoverComma::No, | |
| RecoverColon::Yes, | |
| CommaRecoveryMode::LikelyTuple, | |
| )?)), | |
| AstFragmentKind::Crate => AstFragment::Crate(this.parse_crate_mod()?), | |
| AstFragmentKind::Arms | |
| | AstFragmentKind::ExprFields | |
| | AstFragmentKind::PatFields | |
| | AstFragmentKind::GenericParams | |
| | AstFragmentKind::Params | |
| | AstFragmentKind::FieldDefs | |
| | AstFragmentKind::Variants | |
| | AstFragmentKind::WherePredicates => panic!("unexpected AST fragment kind"), | |
| }) | |
| } | |
| pub(crate) fn ensure_complete_parse<'a>( | |
| parser: &Parser<'a>, | |
| macro_path: &ast::Path, | |
| kind_name: &str, | |
| span: Span, | |
| ) { | |
| if parser.token != token::Eof { | |
| let descr = token_descr(&parser.token); | |
| // Avoid emitting backtrace info twice. | |
| let def_site_span = parser.token.span.with_ctxt(SyntaxContext::root()); | |
| let semi_span = parser.psess.source_map().next_point(span); | |
| let add_semicolon = match &parser.psess.source_map().span_to_snippet(semi_span) { | |
| Ok(snippet) if &snippet[..] != ";" && kind_name == "expression" => { | |
| Some(span.shrink_to_hi()) | |
| } | |
| _ => None, | |
| }; | |
| let expands_to_match_arm = kind_name == "pattern" && parser.token == token::FatArrow; | |
| parser.dcx().emit_err(IncompleteParse { | |
| span: def_site_span, | |
| descr, | |
| label_span: span, | |
| macro_path: pprust::path_to_string(macro_path), | |
| kind_name, | |
| expands_to_match_arm, | |
| add_semicolon, | |
| }); | |
| } | |
| } | |
| /// Wraps a call to `walk_*` / `walk_flat_map_*` | |
| /// for an AST node that supports attributes | |
| /// (see the `Annotatable` enum) | |
| /// This method assigns a `NodeId`, and sets that `NodeId` | |
| /// as our current 'lint node id'. If a macro call is found | |
| /// inside this AST node, we will use this AST node's `NodeId` | |
| /// to emit lints associated with that macro (allowing | |
| /// `#[allow]` / `#[deny]` to be applied close to | |
| /// the macro invocation). | |
| /// | |
| /// Do *not* call this for a macro AST node | |
| /// (e.g. `ExprKind::MacCall`) - we cannot emit lints | |
| /// at these AST nodes, since they are removed and | |
| /// replaced with the result of macro expansion. | |
| /// | |
| /// All other `NodeId`s are assigned by `visit_id`. | |
| /// * `self` is the 'self' parameter for the current method, | |
| /// * `id` is a mutable reference to the `NodeId` field | |
| /// of the current AST node. | |
| /// * `closure` is a closure that executes the | |
| /// `walk_*` / `walk_flat_map_*` method | |
| /// for the current AST node. | |
| macro_rules! assign_id { | |
| ($self:ident, $id:expr, $closure:expr) => {{ | |
| let old_id = $self.cx.current_expansion.lint_node_id; | |
| if $self.monotonic { | |
| debug_assert_eq!(*$id, ast::DUMMY_NODE_ID); | |
| let new_id = $self.cx.resolver.next_node_id(); | |
| *$id = new_id; | |
| $self.cx.current_expansion.lint_node_id = new_id; | |
| } | |
| let ret = ($closure)(); | |
| $self.cx.current_expansion.lint_node_id = old_id; | |
| ret | |
| }}; | |
| } | |
| enum AddSemicolon { | |
| Yes, | |
| No, | |
| } | |
| /// A trait implemented for all `AstFragment` nodes and providing all pieces | |
| /// of functionality used by `InvocationCollector`. | |
| trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized { | |
| type OutputTy = SmallVec<[Self; 1]>; | |
| type ItemKind = ItemKind; | |
| const KIND: AstFragmentKind; | |
| fn to_annotatable(self) -> Annotatable; | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy; | |
| fn descr() -> &'static str { | |
| unreachable!() | |
| } | |
| fn walk_flat_map(self, _collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| unreachable!() | |
| } | |
| fn walk(&mut self, _collector: &mut InvocationCollector<'_, '_>) { | |
| unreachable!() | |
| } | |
| fn is_mac_call(&self) -> bool { | |
| false | |
| } | |
| fn take_mac_call(self) -> (Box<ast::MacCall>, ast::AttrVec, AddSemicolon) { | |
| unreachable!() | |
| } | |
| fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { | |
| None | |
| } | |
| fn delegation_item_kind(_deleg: Box<ast::Delegation>) -> Self::ItemKind { | |
| unreachable!() | |
| } | |
| fn from_item(_item: ast::Item<Self::ItemKind>) -> Self { | |
| unreachable!() | |
| } | |
| fn flatten_outputs(_outputs: impl Iterator<Item = Self::OutputTy>) -> Self::OutputTy { | |
| unreachable!() | |
| } | |
| fn pre_flat_map_node_collect_attr(_cfg: &StripUnconfigured<'_>, _attr: &ast::Attribute) {} | |
| fn post_flat_map_node_collect_bang(_output: &mut Self::OutputTy, _add_semicolon: AddSemicolon) { | |
| } | |
| fn wrap_flat_map_node_walk_flat_map( | |
| node: Self, | |
| collector: &mut InvocationCollector<'_, '_>, | |
| walk_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy, | |
| ) -> Result<Self::OutputTy, Self> { | |
| Ok(walk_flat_map(node, collector)) | |
| } | |
| fn expand_cfg_false( | |
| &mut self, | |
| collector: &mut InvocationCollector<'_, '_>, | |
| _pos: usize, | |
| span: Span, | |
| ) { | |
| collector.cx.dcx().emit_err(RemoveNodeNotSupported { span, descr: Self::descr() }); | |
| } | |
| /// All of the identifiers (items) declared by this node. | |
| /// This is an approximation and should only be used for diagnostics. | |
| fn declared_idents(&self) -> Vec<Ident> { | |
| vec![] | |
| } | |
| fn as_target(&self) -> Target; | |
| } | |
| impl InvocationCollectorNode for Box<ast::Item> { | |
| const KIND: AstFragmentKind = AstFragmentKind::Items; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::Item(self) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_items() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_item(collector, self) | |
| } | |
| fn is_mac_call(&self) -> bool { | |
| matches!(self.kind, ItemKind::MacCall(..)) | |
| } | |
| fn take_mac_call(self) -> (Box<ast::MacCall>, ast::AttrVec, AddSemicolon) { | |
| match self.kind { | |
| ItemKind::MacCall(mac) => (mac, self.attrs, AddSemicolon::No), | |
| _ => unreachable!(), | |
| } | |
| } | |
| fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { | |
| match &self.kind { | |
| ItemKind::DelegationMac(deleg) => Some((deleg, self)), | |
| _ => None, | |
| } | |
| } | |
| fn delegation_item_kind(deleg: Box<ast::Delegation>) -> Self::ItemKind { | |
| ItemKind::Delegation(deleg) | |
| } | |
| fn from_item(item: ast::Item<Self::ItemKind>) -> Self { | |
| Box::new(item) | |
| } | |
| fn flatten_outputs(items: impl Iterator<Item = Self::OutputTy>) -> Self::OutputTy { | |
| items.flatten().collect() | |
| } | |
| fn wrap_flat_map_node_walk_flat_map( | |
| mut node: Self, | |
| collector: &mut InvocationCollector<'_, '_>, | |
| walk_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy, | |
| ) -> Result<Self::OutputTy, Self> { | |
| if !matches!(node.kind, ItemKind::Mod(..)) { | |
| return Ok(walk_flat_map(node, collector)); | |
| } | |
| let ItemKind::Mod(_, ident, ref mut mod_kind) = node.kind else { unreachable!() }; | |
| let ecx = &mut collector.cx; | |
| 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( | |
| ecx.sess, | |
| ident, | |
| &node.attrs, | |
| &ecx.current_expansion.module, | |
| ecx.current_expansion.dir_ownership, | |
| *inline, | |
| ); | |
| // If the module was parsed from an external file, recover its path. | |
| // This lets `parse_external_mod` catch cycles if it's self-referential. | |
| let file_path = match inline { | |
| Inline::Yes => None, | |
| Inline::No { .. } => mod_file_path_from_attr(ecx.sess, &node.attrs, &dir_path), | |
| }; | |
| (file_path, dir_path, dir_ownership) | |
| } | |
| ModKind::Unloaded => { | |
| // We have an outline `mod foo;` so we need to parse the file. | |
| let old_attrs_len = node.attrs.len(); | |
| let ParsedExternalMod { | |
| items, | |
| spans, | |
| file_path, | |
| dir_path, | |
| dir_ownership, | |
| had_parse_error, | |
| } = parse_external_mod( | |
| ecx.sess, | |
| ident, | |
| node.span, | |
| &ecx.current_expansion.module, | |
| ecx.current_expansion.dir_ownership, | |
| &mut node.attrs, | |
| ); | |
| if let Some(lint_store) = ecx.lint_store { | |
| lint_store.pre_expansion_lint( | |
| ecx.sess, | |
| ecx.ecfg.features, | |
| ecx.resolver.registered_lint_tools(), | |
| ecx.current_expansion.lint_node_id, | |
| &node.attrs, | |
| &items, | |
| ident.name, | |
| ); | |
| } | |
| *mod_kind = ModKind::Loaded(items, Inline::No { had_parse_error }, spans); | |
| if node.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. | |
| return Err(node); | |
| } | |
| (Some(file_path), dir_path, dir_ownership) | |
| } | |
| }; | |
| // Set the module info before we flat map. | |
| let mut module = ecx.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 ecx.current_expansion.module, Rc::new(module)); | |
| let orig_dir_ownership = | |
| mem::replace(&mut ecx.current_expansion.dir_ownership, dir_ownership); | |
| let res = Ok(walk_flat_map(node, collector)); | |
| collector.cx.current_expansion.dir_ownership = orig_dir_ownership; | |
| collector.cx.current_expansion.module = orig_module; | |
| res | |
| } | |
| fn declared_idents(&self) -> Vec<Ident> { | |
| if let ItemKind::Use(ut) = &self.kind { | |
| fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec<Ident>) { | |
| match &ut.kind { | |
| ast::UseTreeKind::Glob(_) => {} | |
| ast::UseTreeKind::Simple(_) => idents.push(ut.ident()), | |
| ast::UseTreeKind::Nested { items, .. } => { | |
| for (ut, _) in items { | |
| collect_use_tree_leaves(ut, idents); | |
| } | |
| } | |
| } | |
| } | |
| let mut idents = Vec::new(); | |
| collect_use_tree_leaves(ut, &mut idents); | |
| idents | |
| } else { | |
| self.kind.ident().into_iter().collect() | |
| } | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::from_ast_item(self) | |
| } | |
| } | |
| struct TraitItemTag; | |
| impl InvocationCollectorNode for AstNodeWrapper<Box<ast::AssocItem>, TraitItemTag> { | |
| type OutputTy = SmallVec<[Box<ast::AssocItem>; 1]>; | |
| type ItemKind = AssocItemKind; | |
| const KIND: AstFragmentKind = AstFragmentKind::TraitItems; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::AssocItem(self.wrapped, AssocCtxt::Trait) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_trait_items() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_assoc_item(collector, self.wrapped, AssocCtxt::Trait) | |
| } | |
| fn is_mac_call(&self) -> bool { | |
| matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) | |
| } | |
| fn take_mac_call(self) -> (Box<ast::MacCall>, ast::AttrVec, AddSemicolon) { | |
| let item = self.wrapped; | |
| match item.kind { | |
| AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), | |
| _ => unreachable!(), | |
| } | |
| } | |
| fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { | |
| match &self.wrapped.kind { | |
| AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), | |
| _ => None, | |
| } | |
| } | |
| fn delegation_item_kind(deleg: Box<ast::Delegation>) -> Self::ItemKind { | |
| AssocItemKind::Delegation(deleg) | |
| } | |
| fn from_item(item: ast::Item<Self::ItemKind>) -> Self { | |
| AstNodeWrapper::new(Box::new(item), TraitItemTag) | |
| } | |
| fn flatten_outputs(items: impl Iterator<Item = Self::OutputTy>) -> Self::OutputTy { | |
| items.flatten().collect() | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::from_assoc_item_kind(&self.wrapped.kind, AssocCtxt::Trait) | |
| } | |
| } | |
| struct ImplItemTag; | |
| impl InvocationCollectorNode for AstNodeWrapper<Box<ast::AssocItem>, ImplItemTag> { | |
| type OutputTy = SmallVec<[Box<ast::AssocItem>; 1]>; | |
| type ItemKind = AssocItemKind; | |
| const KIND: AstFragmentKind = AstFragmentKind::ImplItems; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::AssocItem(self.wrapped, AssocCtxt::Impl { of_trait: false }) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_impl_items() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_assoc_item(collector, self.wrapped, AssocCtxt::Impl { of_trait: false }) | |
| } | |
| fn is_mac_call(&self) -> bool { | |
| matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) | |
| } | |
| fn take_mac_call(self) -> (Box<ast::MacCall>, ast::AttrVec, AddSemicolon) { | |
| let item = self.wrapped; | |
| match item.kind { | |
| AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), | |
| _ => unreachable!(), | |
| } | |
| } | |
| fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { | |
| match &self.wrapped.kind { | |
| AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), | |
| _ => None, | |
| } | |
| } | |
| fn delegation_item_kind(deleg: Box<ast::Delegation>) -> Self::ItemKind { | |
| AssocItemKind::Delegation(deleg) | |
| } | |
| fn from_item(item: ast::Item<Self::ItemKind>) -> Self { | |
| AstNodeWrapper::new(Box::new(item), ImplItemTag) | |
| } | |
| fn flatten_outputs(items: impl Iterator<Item = Self::OutputTy>) -> Self::OutputTy { | |
| items.flatten().collect() | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::from_assoc_item_kind(&self.wrapped.kind, AssocCtxt::Impl { of_trait: false }) | |
| } | |
| } | |
| struct TraitImplItemTag; | |
| impl InvocationCollectorNode for AstNodeWrapper<Box<ast::AssocItem>, TraitImplItemTag> { | |
| type OutputTy = SmallVec<[Box<ast::AssocItem>; 1]>; | |
| type ItemKind = AssocItemKind; | |
| const KIND: AstFragmentKind = AstFragmentKind::TraitImplItems; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::AssocItem(self.wrapped, AssocCtxt::Impl { of_trait: true }) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_trait_impl_items() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_assoc_item(collector, self.wrapped, AssocCtxt::Impl { of_trait: true }) | |
| } | |
| fn is_mac_call(&self) -> bool { | |
| matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) | |
| } | |
| fn take_mac_call(self) -> (Box<ast::MacCall>, ast::AttrVec, AddSemicolon) { | |
| let item = self.wrapped; | |
| match item.kind { | |
| AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), | |
| _ => unreachable!(), | |
| } | |
| } | |
| fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { | |
| match &self.wrapped.kind { | |
| AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)), | |
| _ => None, | |
| } | |
| } | |
| fn delegation_item_kind(deleg: Box<ast::Delegation>) -> Self::ItemKind { | |
| AssocItemKind::Delegation(deleg) | |
| } | |
| fn from_item(item: ast::Item<Self::ItemKind>) -> Self { | |
| AstNodeWrapper::new(Box::new(item), TraitImplItemTag) | |
| } | |
| fn flatten_outputs(items: impl Iterator<Item = Self::OutputTy>) -> Self::OutputTy { | |
| items.flatten().collect() | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::from_assoc_item_kind(&self.wrapped.kind, AssocCtxt::Impl { of_trait: true }) | |
| } | |
| } | |
| impl InvocationCollectorNode for Box<ast::ForeignItem> { | |
| const KIND: AstFragmentKind = AstFragmentKind::ForeignItems; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::ForeignItem(self) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_foreign_items() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_foreign_item(collector, self) | |
| } | |
| fn is_mac_call(&self) -> bool { | |
| matches!(self.kind, ForeignItemKind::MacCall(..)) | |
| } | |
| fn take_mac_call(self) -> (Box<ast::MacCall>, ast::AttrVec, AddSemicolon) { | |
| match self.kind { | |
| ForeignItemKind::MacCall(mac) => (mac, self.attrs, AddSemicolon::No), | |
| _ => unreachable!(), | |
| } | |
| } | |
| fn as_target(&self) -> Target { | |
| match &self.kind { | |
| ForeignItemKind::Static(_) => Target::ForeignStatic, | |
| ForeignItemKind::Fn(_) => Target::ForeignFn, | |
| ForeignItemKind::TyAlias(_) => Target::ForeignTy, | |
| ForeignItemKind::MacCall(_) => Target::MacroCall, | |
| } | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::Variant { | |
| const KIND: AstFragmentKind = AstFragmentKind::Variants; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::Variant(self) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_variants() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_variant(collector, self) | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::Variant | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::WherePredicate { | |
| const KIND: AstFragmentKind = AstFragmentKind::WherePredicates; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::WherePredicate(self) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_where_predicates() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_where_predicate(collector, self) | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::WherePredicate | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::FieldDef { | |
| const KIND: AstFragmentKind = AstFragmentKind::FieldDefs; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::FieldDef(self) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_field_defs() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_field_def(collector, self) | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::Field | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::PatField { | |
| const KIND: AstFragmentKind = AstFragmentKind::PatFields; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::PatField(self) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_pat_fields() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_pat_field(collector, self) | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::PatField | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::ExprField { | |
| const KIND: AstFragmentKind = AstFragmentKind::ExprFields; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::ExprField(self) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_expr_fields() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_expr_field(collector, self) | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::ExprField | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::Param { | |
| const KIND: AstFragmentKind = AstFragmentKind::Params; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::Param(self) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_params() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_param(collector, self) | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::Param | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::GenericParam { | |
| const KIND: AstFragmentKind = AstFragmentKind::GenericParams; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::GenericParam(self) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_generic_params() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_generic_param(collector, self) | |
| } | |
| fn as_target(&self) -> Target { | |
| let mut has_default = false; | |
| Target::GenericParam { | |
| kind: match &self.kind { | |
| rustc_ast::GenericParamKind::Lifetime => { | |
| rustc_hir::target::GenericParamKind::Lifetime | |
| } | |
| rustc_ast::GenericParamKind::Type { default } => { | |
| has_default = default.is_some(); | |
| rustc_hir::target::GenericParamKind::Type | |
| } | |
| rustc_ast::GenericParamKind::Const { default, .. } => { | |
| has_default = default.is_some(); | |
| rustc_hir::target::GenericParamKind::Const | |
| } | |
| }, | |
| has_default, | |
| } | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::Arm { | |
| const KIND: AstFragmentKind = AstFragmentKind::Arms; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::Arm(self) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_arms() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_arm(collector, self) | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::Arm | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::Stmt { | |
| const KIND: AstFragmentKind = AstFragmentKind::Stmts; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::Stmt(Box::new(self)) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_stmts() | |
| } | |
| fn walk_flat_map(self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_flat_map_stmt(collector, self) | |
| } | |
| fn is_mac_call(&self) -> bool { | |
| match &self.kind { | |
| StmtKind::MacCall(..) => true, | |
| StmtKind::Item(item) => matches!(item.kind, ItemKind::MacCall(..)), | |
| StmtKind::Semi(expr) => matches!(expr.kind, ExprKind::MacCall(..)), | |
| StmtKind::Expr(..) => unreachable!(), | |
| StmtKind::Let(..) | StmtKind::Empty => false, | |
| } | |
| } | |
| fn take_mac_call(self) -> (Box<ast::MacCall>, ast::AttrVec, AddSemicolon) { | |
| // We pull macro invocations (both attributes and fn-like macro calls) out of their | |
| // `StmtKind`s and treat them as statement macro invocations, not as items or expressions. | |
| let (add_semicolon, mac, attrs) = match self.kind { | |
| StmtKind::MacCall(mac) => { | |
| let ast::MacCallStmt { mac, style, attrs, .. } = *mac; | |
| (style == MacStmtStyle::Semicolon, mac, attrs) | |
| } | |
| StmtKind::Item(item) => match *item { | |
| ast::Item { kind: ItemKind::MacCall(mac), attrs, .. } => { | |
| (mac.args.need_semicolon(), mac, attrs) | |
| } | |
| _ => unreachable!(), | |
| }, | |
| StmtKind::Semi(expr) => match *expr { | |
| ast::Expr { kind: ExprKind::MacCall(mac), attrs, .. } => { | |
| (mac.args.need_semicolon(), mac, attrs) | |
| } | |
| _ => unreachable!(), | |
| }, | |
| _ => unreachable!(), | |
| }; | |
| (mac, attrs, if add_semicolon { AddSemicolon::Yes } else { AddSemicolon::No }) | |
| } | |
| fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> { | |
| match &self.kind { | |
| StmtKind::Item(item) => match &item.kind { | |
| ItemKind::DelegationMac(deleg) => Some((deleg, item)), | |
| _ => None, | |
| }, | |
| _ => None, | |
| } | |
| } | |
| fn delegation_item_kind(deleg: Box<ast::Delegation>) -> Self::ItemKind { | |
| ItemKind::Delegation(deleg) | |
| } | |
| fn from_item(item: ast::Item<Self::ItemKind>) -> Self { | |
| ast::Stmt { id: ast::DUMMY_NODE_ID, span: item.span, kind: StmtKind::Item(Box::new(item)) } | |
| } | |
| fn flatten_outputs(items: impl Iterator<Item = Self::OutputTy>) -> Self::OutputTy { | |
| items.flatten().collect() | |
| } | |
| fn post_flat_map_node_collect_bang(stmts: &mut Self::OutputTy, add_semicolon: AddSemicolon) { | |
| // If this is a macro invocation with a semicolon, then apply that | |
| // semicolon to the final statement produced by expansion. | |
| if matches!(add_semicolon, AddSemicolon::Yes) { | |
| if let Some(stmt) = stmts.pop() { | |
| stmts.push(stmt.add_trailing_semicolon()); | |
| } | |
| } | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::Statement | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::Crate { | |
| type OutputTy = ast::Crate; | |
| const KIND: AstFragmentKind = AstFragmentKind::Crate; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::Crate(self) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_crate() | |
| } | |
| fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) { | |
| walk_crate(collector, self) | |
| } | |
| fn expand_cfg_false( | |
| &mut self, | |
| collector: &mut InvocationCollector<'_, '_>, | |
| pos: usize, | |
| _span: Span, | |
| ) { | |
| // Attributes above `cfg(FALSE)` are left in place, because we may want to configure | |
| // some global crate properties even on fully unconfigured crates. | |
| self.attrs.truncate(pos); | |
| // Standard prelude imports are left in the crate for backward compatibility. | |
| self.items.truncate(collector.cx.num_standard_library_imports); | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::Crate | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::Ty { | |
| type OutputTy = Box<ast::Ty>; | |
| const KIND: AstFragmentKind = AstFragmentKind::Ty; | |
| fn to_annotatable(self) -> Annotatable { | |
| unreachable!() | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_ty() | |
| } | |
| fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) { | |
| // Save the pre-expanded name of this `ImplTrait`, so that later when defining | |
| // an APIT we use a name that doesn't have any placeholder fragments in it. | |
| if let ast::TyKind::ImplTrait(..) = self.kind { | |
| // HACK: pprust breaks strings with newlines when the type | |
| // gets too long. We don't want these to show up in compiler | |
| // output or built artifacts, so replace them here... | |
| // Perhaps we should instead format APITs more robustly. | |
| let name = Symbol::intern(&pprust::ty_to_string(self).replace('\n', " ")); | |
| collector.cx.resolver.insert_impl_trait_name(self.id, name); | |
| } | |
| walk_ty(collector, self) | |
| } | |
| fn is_mac_call(&self) -> bool { | |
| matches!(self.kind, ast::TyKind::MacCall(..)) | |
| } | |
| fn take_mac_call(self) -> (Box<ast::MacCall>, ast::AttrVec, AddSemicolon) { | |
| match self.kind { | |
| TyKind::MacCall(mac) => (mac, AttrVec::new(), AddSemicolon::No), | |
| _ => unreachable!(), | |
| } | |
| } | |
| fn as_target(&self) -> Target { | |
| // This is only used for attribute parsing, which are not allowed on types. | |
| unreachable!() | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::Pat { | |
| type OutputTy = Box<ast::Pat>; | |
| const KIND: AstFragmentKind = AstFragmentKind::Pat; | |
| fn to_annotatable(self) -> Annotatable { | |
| unreachable!() | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_pat() | |
| } | |
| fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) { | |
| walk_pat(collector, self) | |
| } | |
| fn is_mac_call(&self) -> bool { | |
| matches!(self.kind, PatKind::MacCall(..)) | |
| } | |
| fn take_mac_call(self) -> (Box<ast::MacCall>, ast::AttrVec, AddSemicolon) { | |
| match self.kind { | |
| PatKind::MacCall(mac) => (mac, AttrVec::new(), AddSemicolon::No), | |
| _ => unreachable!(), | |
| } | |
| } | |
| fn as_target(&self) -> Target { | |
| unimplemented!(); | |
| } | |
| } | |
| impl InvocationCollectorNode for ast::Expr { | |
| type OutputTy = Box<ast::Expr>; | |
| const KIND: AstFragmentKind = AstFragmentKind::Expr; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::Expr(Box::new(self)) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_expr() | |
| } | |
| fn descr() -> &'static str { | |
| "an expression" | |
| } | |
| fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) { | |
| walk_expr(collector, self) | |
| } | |
| fn is_mac_call(&self) -> bool { | |
| matches!(self.kind, ExprKind::MacCall(..)) | |
| } | |
| fn take_mac_call(self) -> (Box<ast::MacCall>, ast::AttrVec, AddSemicolon) { | |
| match self.kind { | |
| ExprKind::MacCall(mac) => (mac, self.attrs, AddSemicolon::No), | |
| _ => unreachable!(), | |
| } | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::Expression | |
| } | |
| } | |
| struct OptExprTag; | |
| impl InvocationCollectorNode for AstNodeWrapper<Box<ast::Expr>, OptExprTag> { | |
| type OutputTy = Option<Box<ast::Expr>>; | |
| const KIND: AstFragmentKind = AstFragmentKind::OptExpr; | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::Expr(self.wrapped) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| fragment.make_opt_expr() | |
| } | |
| fn walk_flat_map(mut self, collector: &mut InvocationCollector<'_, '_>) -> Self::OutputTy { | |
| walk_expr(collector, &mut self.wrapped); | |
| Some(self.wrapped) | |
| } | |
| fn is_mac_call(&self) -> bool { | |
| matches!(self.wrapped.kind, ast::ExprKind::MacCall(..)) | |
| } | |
| fn take_mac_call(self) -> (Box<ast::MacCall>, ast::AttrVec, AddSemicolon) { | |
| let node = self.wrapped; | |
| match node.kind { | |
| ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), | |
| _ => unreachable!(), | |
| } | |
| } | |
| fn pre_flat_map_node_collect_attr(cfg: &StripUnconfigured<'_>, attr: &ast::Attribute) { | |
| cfg.maybe_emit_expr_attr_err(attr); | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::Expression | |
| } | |
| } | |
| /// This struct is a hack to workaround unstable of `stmt_expr_attributes`. | |
| /// It can be removed once that feature is stabilized. | |
| struct MethodReceiverTag; | |
| impl InvocationCollectorNode for AstNodeWrapper<ast::Expr, MethodReceiverTag> { | |
| type OutputTy = AstNodeWrapper<Box<ast::Expr>, MethodReceiverTag>; | |
| const KIND: AstFragmentKind = AstFragmentKind::MethodReceiverExpr; | |
| fn descr() -> &'static str { | |
| "an expression" | |
| } | |
| fn to_annotatable(self) -> Annotatable { | |
| Annotatable::Expr(Box::new(self.wrapped)) | |
| } | |
| fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { | |
| AstNodeWrapper::new(fragment.make_method_receiver_expr(), MethodReceiverTag) | |
| } | |
| fn walk(&mut self, collector: &mut InvocationCollector<'_, '_>) { | |
| walk_expr(collector, &mut self.wrapped) | |
| } | |
| fn is_mac_call(&self) -> bool { | |
| matches!(self.wrapped.kind, ast::ExprKind::MacCall(..)) | |
| } | |
| fn take_mac_call(self) -> (Box<ast::MacCall>, ast::AttrVec, AddSemicolon) { | |
| let node = self.wrapped; | |
| match node.kind { | |
| ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), | |
| _ => unreachable!(), | |
| } | |
| } | |
| fn as_target(&self) -> Target { | |
| Target::Expression | |
| } | |
| } | |
| fn build_single_delegations<'a, Node: InvocationCollectorNode>( | |
| ecx: &ExtCtxt<'_>, | |
| deleg: &'a ast::DelegationMac, | |
| item: &'a ast::Item<Node::ItemKind>, | |
| suffixes: &'a [(Ident, Option<Ident>)], | |
| item_span: Span, | |
| source: DelegationSource, | |
| ) -> impl Iterator<Item = ast::Item<Node::ItemKind>> + 'a { | |
| debug_assert_ne!(source, DelegationSource::Single); | |
| let from_glob = source == DelegationSource::Glob; | |
| if suffixes.is_empty() { | |
| // Report an error for now, to avoid keeping stem for resolution and | |
| // stability checks. | |
| let kind = String::from(if from_glob { "glob" } else { "list" }); | |
| ecx.dcx().emit_err(EmptyDelegationMac { span: item.span, kind }); | |
| } | |
| suffixes.iter().map(move |&(ident, rename)| { | |
| let mut path = deleg.prefix.clone(); | |
| path.segments.push(ast::PathSegment { ident, id: ast::DUMMY_NODE_ID, args: None }); | |
| ast::Item { | |
| attrs: item.attrs.clone(), | |
| id: ast::DUMMY_NODE_ID, | |
| span: if from_glob { item_span } else { ident.span }, | |
| vis: item.vis.clone(), | |
| kind: Node::delegation_item_kind(Box::new(ast::Delegation { | |
| id: ast::DUMMY_NODE_ID, | |
| qself: deleg.qself.clone(), | |
| path, | |
| ident: rename.unwrap_or(ident), | |
| rename, | |
| body: deleg.body.clone(), | |
| source, | |
| })), | |
| tokens: None, | |
| } | |
| }) | |
| } | |
| /// Required for `visit_node` obtained an owned `Node` from `&mut Node`. | |
| trait DummyAstNode { | |
| fn dummy() -> Self; | |
| } | |
| impl DummyAstNode for ast::Crate { | |
| fn dummy() -> Self { | |
| ast::Crate { | |
| attrs: Default::default(), | |
| items: Default::default(), | |
| spans: Default::default(), | |
| id: DUMMY_NODE_ID, | |
| is_placeholder: Default::default(), | |
| } | |
| } | |
| } | |
| impl DummyAstNode for ast::Ty { | |
| fn dummy() -> Self { | |
| ast::Ty { id: DUMMY_NODE_ID, kind: TyKind::Dummy, span: Default::default() } | |
| } | |
| } | |
| impl DummyAstNode for ast::Pat { | |
| fn dummy() -> Self { | |
| ast::Pat { id: DUMMY_NODE_ID, kind: PatKind::Wild, span: Default::default() } | |
| } | |
| } | |
| impl DummyAstNode for ast::Expr { | |
| fn dummy() -> Self { | |
| ast::Expr::dummy() | |
| } | |
| } | |
| impl DummyAstNode for AstNodeWrapper<ast::Expr, MethodReceiverTag> { | |
| fn dummy() -> Self { | |
| AstNodeWrapper::new(ast::Expr::dummy(), MethodReceiverTag) | |
| } | |
| } | |
| struct InvocationCollector<'a, 'b> { | |
| cx: &'a mut ExtCtxt<'b>, | |
| invocations: Vec<(Invocation, Option<Arc<SyntaxExtension>>)>, | |
| monotonic: bool, | |
| } | |
| impl<'a, 'b> InvocationCollector<'a, 'b> { | |
| fn cfg(&self) -> StripUnconfigured<'_> { | |
| StripUnconfigured { | |
| sess: self.cx.sess, | |
| features: Some(self.cx.ecfg.features), | |
| config_tokens: false, | |
| lint_node_id: self.cx.current_expansion.lint_node_id, | |
| } | |
| } | |
| fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment { | |
| let expn_id = LocalExpnId::fresh_empty(); | |
| if matches!(kind, InvocationKind::GlobDelegation { .. }) { | |
| // In resolver we need to know which invocation ids are delegations early, | |
| // before their `ExpnData` is filled. | |
| self.cx.resolver.register_glob_delegation(expn_id); | |
| } | |
| 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: Box<ast::MacCall>, kind: AstFragmentKind) -> AstFragment { | |
| // cache the macro call span so that it can be | |
| // easily adjusted for incremental compilation | |
| let span = mac.span(); | |
| self.collect(kind, InvocationKind::Bang { mac, span }) | |
| } | |
| fn collect_attr( | |
| &mut self, | |
| (attr, pos, derives): (ast::Attribute, usize, Vec<ast::Path>), | |
| item: Annotatable, | |
| kind: AstFragmentKind, | |
| ) -> AstFragment { | |
| self.collect(kind, InvocationKind::Attr { attr, pos, item, derives }) | |
| } | |
| fn collect_glob_delegation( | |
| &mut self, | |
| item: Box<ast::AssocItem>, | |
| of_trait: bool, | |
| kind: AstFragmentKind, | |
| ) -> AstFragment { | |
| self.collect(kind, InvocationKind::GlobDelegation { item, of_trait }) | |
| } | |
| /// 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( | |
| &self, | |
| item: &mut impl HasAttrs, | |
| ) -> Option<(ast::Attribute, usize, Vec<ast::Path>)> { | |
| let mut attr = None; | |
| let mut cfg_pos = None; | |
| let mut attr_pos = None; | |
| for (pos, attr) in item.attrs().iter().enumerate() { | |
| if let AttrKind::Normal(..) = attr.kind | |
| && !self.cx.expanded_inert_attrs.is_marked(attr) | |
| { | |
| let name = attr.name(); | |
| if name == Some(sym::cfg) || name == Some(sym::cfg_attr) { | |
| cfg_pos = Some(pos); // a cfg attr found, no need to search anymore | |
| break; | |
| } else if attr_pos.is_none() | |
| && !name.is_some_and(rustc_feature::is_builtin_attr_name) | |
| { | |
| attr_pos = Some(pos); // a non-cfg attr found, still may find a cfg attr | |
| } | |
| } | |
| } | |
| item.visit_attrs(|attrs| { | |
| attr = Some(match (cfg_pos, attr_pos) { | |
| (Some(pos), _) => (attrs.remove(pos), pos, Vec::new()), | |
| (_, Some(pos)) => { | |
| let attr = attrs.remove(pos); | |
| let following_derives = attrs[pos..] | |
| .iter() | |
| .filter(|a| a.has_name(sym::derive)) | |
| .flat_map(|a| a.meta_item_list().unwrap_or_default()) | |
| .filter_map(|meta_item_inner| match meta_item_inner { | |
| MetaItemInner::MetaItem(ast::MetaItem { | |
| kind: MetaItemKind::Word, | |
| path, | |
| .. | |
| }) => Some(path), | |
| _ => None, | |
| }) | |
| .collect(); | |
| (attr, pos, following_derives) | |
| } | |
| _ => return, | |
| }); | |
| }); | |
| attr | |
| } | |
| // Detect use of feature-gated or invalid attributes on macro invocations | |
| // since they will not be detected after macro expansion. | |
| fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) { | |
| use SyntheticAttr::*; | |
| let features = self.cx.ecfg.features; | |
| 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_attr(&self.cx.sess.psess, attr); | |
| AttributeParser::parse_limited_all( | |
| self.cx.sess, | |
| slice::from_ref(attr), | |
| None, | |
| Target::MacroCall, | |
| call.span(), | |
| self.cx.current_expansion.lint_node_id, | |
| Some(self.cx.ecfg.features), | |
| ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, | |
| Some(self.cx.resolver.registered_attr_tools()), | |
| ); | |
| let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; | |
| span = Some(current_span); | |
| if attrs.peek().is_some_and(|next_attr| next_attr.doc_str().is_some()) { | |
| continue; | |
| } | |
| if attr.doc_str_and_fragment_kind().is_some() { | |
| self.cx.sess.psess.buffer_lint( | |
| UNUSED_DOC_COMMENTS, | |
| current_span, | |
| self.cx.current_expansion.lint_node_id, | |
| crate::diagnostics::MacroCallUnusedDocComment { span: attr.span }, | |
| ); | |
| continue; | |
| } | |
| match &attr.kind { | |
| AttrKind::Normal(normal) | |
| if rustc_attr_parsing::is_builtin_attr(&normal.item) | |
| && !AttributeParser::is_parsed_attribute(&attr.path()) => | |
| { | |
| let attr_name = attr.name().unwrap(); | |
| self.cx.sess.psess.buffer_lint( | |
| UNUSED_ATTRIBUTES, | |
| attr.span, | |
| self.cx.current_expansion.lint_node_id, | |
| crate::diagnostics::UnusedBuiltinAttribute { | |
| attr_name, | |
| macro_name: pprust::path_to_string(&call.path), | |
| invoc_span: call.path.span, | |
| attr_span: attr.span, | |
| }, | |
| ); | |
| } | |
| AttrKind::Normal(_) => {} | |
| AttrKind::Synthetic(CfgTrace(_) | CfgAttrTrace(_)) => {} | |
| AttrKind::DocComment(..) => unreachable!(), // handled above | |
| } | |
| } | |
| } | |
| fn expand_cfg_true( | |
| &mut self, | |
| node: &mut impl InvocationCollectorNode, | |
| attr: ast::Attribute, | |
| pos: usize, | |
| ) -> EvalConfigResult { | |
| let Some(cfg) = AttributeParser::parse_single( | |
| self.cfg().sess, | |
| &attr, | |
| attr.span, | |
| self.cfg().lint_node_id, | |
| node.as_target(), | |
| self.cfg().features, | |
| ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, | |
| parse_cfg, | |
| &CFG_TEMPLATE, | |
| AllowExprMetavar::Yes, | |
| AttributeSafety::Normal, | |
| ) else { | |
| // Cfg attribute was not parsable, give up | |
| return EvalConfigResult::True; | |
| }; | |
| let res = eval_config_entry(self.cfg().sess, &cfg); | |
| if res.as_bool() { | |
| // A synthetic trace attribute left in AST in place of the original `cfg` attribute. | |
| // It can later be used by lints or other diagnostics. | |
| let trace_attr = attr.convert_normal_to_synthetic(SyntheticAttr::CfgTrace(cfg)); | |
| node.visit_attrs(|attrs| attrs.insert(pos, trace_attr)); | |
| } | |
| res | |
| } | |
| fn expand_cfg_attr(&self, node: &mut impl HasAttrs, attr: &ast::Attribute, pos: usize) { | |
| node.visit_attrs(|attrs| { | |
| // Repeated `insert` calls is inefficient, but the number of | |
| // insertions is almost always 0 or 1 in practice. | |
| for cfg in self.cfg().expand_cfg_attr(attr, false).into_iter().rev() { | |
| attrs.insert(pos, cfg) | |
| } | |
| }); | |
| } | |
| fn flat_map_node<Node: InvocationCollectorNode<OutputTy: Default>>( | |
| &mut self, | |
| mut node: Node, | |
| ) -> Node::OutputTy { | |
| loop { | |
| return match self.take_first_attr(&mut node) { | |
| Some((attr, pos, derives)) => match attr.name() { | |
| Some(sym::cfg) => { | |
| let res = self.expand_cfg_true(&mut node, attr, pos); | |
| match res { | |
| EvalConfigResult::True => continue, | |
| EvalConfigResult::False { reason, reason_span } => { | |
| for ident in node.declared_idents() { | |
| self.cx.resolver.append_stripped_cfg_item( | |
| self.cx.current_expansion.lint_node_id, | |
| ident, | |
| reason.clone(), | |
| reason_span, | |
| ) | |
| } | |
| } | |
| } | |
| Default::default() | |
| } | |
| Some(sym::cfg_attr) => { | |
| self.expand_cfg_attr(&mut node, &attr, pos); | |
| continue; | |
| } | |
| _ => { | |
| Node::pre_flat_map_node_collect_attr(&self.cfg(), &attr); | |
| self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND) | |
| .make_ast::<Node>() | |
| } | |
| }, | |
| None if node.is_mac_call() => { | |
| let (mac, attrs, add_semicolon) = node.take_mac_call(); | |
| self.check_attributes(&attrs, &mac); | |
| let mut res = self.collect_bang(mac, Node::KIND).make_ast::<Node>(); | |
| Node::post_flat_map_node_collect_bang(&mut res, add_semicolon); | |
| res | |
| } | |
| None if let Some((deleg, item)) = node.delegation() => { | |
| let DelegationSuffixes::List(suffixes) = &deleg.suffixes else { | |
| let traitless_qself = | |
| matches!(&deleg.qself, Some(qself) if qself.position == 0); | |
| let (item, of_trait) = match node.to_annotatable() { | |
| Annotatable::AssocItem(item, AssocCtxt::Impl { of_trait }) => { | |
| (item, of_trait) | |
| } | |
| ann @ (Annotatable::Item(_) | |
| | Annotatable::AssocItem(..) | |
| | Annotatable::Stmt(_)) => { | |
| let span = ann.span(); | |
| self.cx.dcx().emit_err(GlobDelegationOutsideImpls { span }); | |
| return Default::default(); | |
| } | |
| _ => unreachable!(), | |
| }; | |
| if traitless_qself { | |
| let span = item.span; | |
| self.cx.dcx().emit_err(GlobDelegationTraitlessQpath { span }); | |
| return Default::default(); | |
| } | |
| return self | |
| .collect_glob_delegation(item, of_trait, Node::KIND) | |
| .make_ast::<Node>(); | |
| }; | |
| let single_delegations = build_single_delegations::<Node>( | |
| self.cx, | |
| deleg, | |
| item, | |
| suffixes, | |
| item.span, | |
| DelegationSource::List(LocalExpnId::fresh_empty()), | |
| ); | |
| Node::flatten_outputs(single_delegations.map(|item| { | |
| let mut item = Node::from_item(item); | |
| assign_id!(self, item.node_id_mut(), || item.walk_flat_map(self)) | |
| })) | |
| } | |
| None => { | |
| match Node::wrap_flat_map_node_walk_flat_map(node, self, |mut node, this| { | |
| assign_id!(this, node.node_id_mut(), || node.walk_flat_map(this)) | |
| }) { | |
| Ok(output) => output, | |
| Err(returned_node) => { | |
| node = returned_node; | |
| continue; | |
| } | |
| } | |
| } | |
| }; | |
| } | |
| } | |
| fn visit_node<Node: InvocationCollectorNode<OutputTy: Into<Node>> + DummyAstNode>( | |
| &mut self, | |
| node: &mut Node, | |
| ) { | |
| loop { | |
| return match self.take_first_attr(node) { | |
| Some((attr, pos, derives)) => match attr.name() { | |
| Some(sym::cfg) => { | |
| let span = attr.span; | |
| if self.expand_cfg_true(node, attr, pos).as_bool() { | |
| continue; | |
| } | |
| node.expand_cfg_false(self, pos, span); | |
| continue; | |
| } | |
| Some(sym::cfg_attr) => { | |
| self.expand_cfg_attr(node, &attr, pos); | |
| continue; | |
| } | |
| _ => { | |
| let n = mem::replace(node, Node::dummy()); | |
| *node = self | |
| .collect_attr((attr, pos, derives), n.to_annotatable(), Node::KIND) | |
| .make_ast::<Node>() | |
| .into() | |
| } | |
| }, | |
| None if node.is_mac_call() => { | |
| let n = mem::replace(node, Node::dummy()); | |
| let (mac, attrs, _) = n.take_mac_call(); | |
| self.check_attributes(&attrs, &mac); | |
| *node = self.collect_bang(mac, Node::KIND).make_ast::<Node>().into() | |
| } | |
| None if node.delegation().is_some() => unreachable!(), | |
| None => { | |
| assign_id!(self, node.node_id_mut(), || node.walk(self)) | |
| } | |
| }; | |
| } | |
| } | |
| } | |
| impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { | |
| fn flat_map_item(&mut self, node: Box<ast::Item>) -> SmallVec<[Box<ast::Item>; 1]> { | |
| self.flat_map_node(node) | |
| } | |
| fn flat_map_assoc_item( | |
| &mut self, | |
| node: Box<ast::AssocItem>, | |
| ctxt: AssocCtxt, | |
| ) -> SmallVec<[Box<ast::AssocItem>; 1]> { | |
| match ctxt { | |
| AssocCtxt::Trait => self.flat_map_node(AstNodeWrapper::new(node, TraitItemTag)), | |
| AssocCtxt::Impl { of_trait: false, .. } => { | |
| self.flat_map_node(AstNodeWrapper::new(node, ImplItemTag)) | |
| } | |
| AssocCtxt::Impl { of_trait: true, .. } => { | |
| self.flat_map_node(AstNodeWrapper::new(node, TraitImplItemTag)) | |
| } | |
| } | |
| } | |
| fn flat_map_foreign_item( | |
| &mut self, | |
| node: Box<ast::ForeignItem>, | |
| ) -> SmallVec<[Box<ast::ForeignItem>; 1]> { | |
| self.flat_map_node(node) | |
| } | |
| fn flat_map_variant(&mut self, node: ast::Variant) -> SmallVec<[ast::Variant; 1]> { | |
| self.flat_map_node(node) | |
| } | |
| fn flat_map_where_predicate( | |
| &mut self, | |
| node: ast::WherePredicate, | |
| ) -> SmallVec<[ast::WherePredicate; 1]> { | |
| self.flat_map_node(node) | |
| } | |
| fn flat_map_field_def(&mut self, node: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> { | |
| self.flat_map_node(node) | |
| } | |
| fn flat_map_pat_field(&mut self, node: ast::PatField) -> SmallVec<[ast::PatField; 1]> { | |
| self.flat_map_node(node) | |
| } | |
| fn flat_map_expr_field(&mut self, node: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> { | |
| self.flat_map_node(node) | |
| } | |
| fn flat_map_param(&mut self, node: ast::Param) -> SmallVec<[ast::Param; 1]> { | |
| self.flat_map_node(node) | |
| } | |
| fn flat_map_generic_param( | |
| &mut self, | |
| node: ast::GenericParam, | |
| ) -> SmallVec<[ast::GenericParam; 1]> { | |
| self.flat_map_node(node) | |
| } | |
| fn flat_map_arm(&mut self, node: ast::Arm) -> SmallVec<[ast::Arm; 1]> { | |
| self.flat_map_node(node) | |
| } | |
| fn flat_map_stmt(&mut self, node: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { | |
| // FIXME: invocations in semicolon-less expressions positions are expanded as expressions, | |
| // changing that requires some compatibility measures. | |
| if node.is_expr() { | |
| // The only way that we can end up with a `MacCall` expression statement, | |
| // (as opposed to a `StmtKind::MacCall`) is if we have a macro as the | |
| // trailing expression in a block (e.g. `fn foo() { my_macro!() }`). | |
| // Record this information, so that we can report a more specific | |
| // `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed. | |
| // See #78991 for an investigation of treating macros in this position | |
| // as statements, rather than expressions, during parsing. | |
| return match &node.kind { | |
| StmtKind::Expr(expr) | |
| if matches!(**expr, ast::Expr { kind: ExprKind::MacCall(..), .. }) => | |
| { | |
| self.cx.current_expansion.is_trailing_mac = true; | |
| // Don't use `assign_id` for this statement - it may get removed | |
| // entirely due to a `#[cfg]` on the contained expression | |
| let res = walk_flat_map_stmt(self, node); | |
| self.cx.current_expansion.is_trailing_mac = false; | |
| res | |
| } | |
| _ => walk_flat_map_stmt(self, node), | |
| }; | |
| } | |
| self.flat_map_node(node) | |
| } | |
| fn visit_crate(&mut self, node: &mut ast::Crate) { | |
| self.visit_node(node) | |
| } | |
| fn visit_ty(&mut self, node: &mut ast::Ty) { | |
| self.visit_node(node) | |
| } | |
| fn visit_pat(&mut self, node: &mut ast::Pat) { | |
| self.visit_node(node) | |
| } | |
| fn visit_expr(&mut self, node: &mut ast::Expr) { | |
| // FIXME: Feature gating is performed inconsistently between `Expr` and `OptExpr`. | |
| if let Some(attr) = node.attrs.first() { | |
| self.cfg().maybe_emit_expr_attr_err(attr); | |
| } | |
| ensure_sufficient_stack(|| self.visit_node(node)) | |
| } | |
| fn visit_method_receiver_expr(&mut self, node: &mut ast::Expr) { | |
| self.visit_node(AstNodeWrapper::from_mut(node, MethodReceiverTag)) | |
| } | |
| fn filter_map_expr(&mut self, node: Box<ast::Expr>) -> Option<Box<ast::Expr>> { | |
| self.flat_map_node(AstNodeWrapper::new(node, OptExprTag)) | |
| } | |
| fn visit_block(&mut self, node: &mut ast::Block) { | |
| let orig_dir_ownership = mem::replace( | |
| &mut self.cx.current_expansion.dir_ownership, | |
| DirOwnership::UnownedViaBlock, | |
| ); | |
| walk_block(self, node); | |
| self.cx.current_expansion.dir_ownership = orig_dir_ownership; | |
| } | |
| fn visit_id(&mut self, id: &mut NodeId) { | |
| // We may have already assigned a `NodeId` | |
| // by calling `assign_id` | |
| if self.monotonic && *id == ast::DUMMY_NODE_ID { | |
| *id = self.cx.resolver.next_node_id(); | |
| } | |
| } | |
| } | |
| pub struct ExpansionConfig<'feat> { | |
| pub crate_name: Symbol, | |
| pub features: &'feat Features, | |
| pub recursion_limit: Limit, | |
| pub trace_mac: bool, | |
| /// If false, strip `#[test]` nodes | |
| pub should_test: bool, | |
| /// If true, use verbose debugging for `proc_macro::Span` | |
| pub span_debug: bool, | |
| /// If true, show backtraces for proc-macro panics | |
| pub proc_macro_backtrace: bool, | |
| } | |
| impl ExpansionConfig<'_> { | |
| pub fn default(crate_name: Symbol, features: &Features) -> ExpansionConfig<'_> { | |
| ExpansionConfig { | |
| crate_name, | |
| features, | |
| // FIXME should this limit be configurable? | |
| recursion_limit: Limit::new(1024), | |
| trace_mac: false, | |
| should_test: false, | |
| span_debug: false, | |
| proc_macro_backtrace: false, | |
| } | |
| } | |
| } |