diff options
Diffstat (limited to 'compiler/rustc_expand/src')
| -rw-r--r-- | compiler/rustc_expand/src/base.rs | 51 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/config.rs | 178 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/expand.rs | 1428 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/lib.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/mbe/macro_parser.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/mbe/macro_rules.rs | 28 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/mbe/transcribe.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/module.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/parse/tests.rs | 29 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/placeholders.rs | 21 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/proc_macro.rs | 28 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/proc_macro_server.rs | 173 |
12 files changed, 1192 insertions, 771 deletions
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index b630bc1f473..258320aeb63 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -8,7 +8,7 @@ use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream}; use rustc_ast::visit::{AssocCtxt, Visitor}; use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, Stability}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::{self, Lrc}; use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported}; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; @@ -48,6 +48,7 @@ pub enum Annotatable { Param(ast::Param), FieldDef(ast::FieldDef), Variant(ast::Variant), + Crate(ast::Crate), } impl Annotatable { @@ -66,6 +67,7 @@ impl Annotatable { Annotatable::Param(ref p) => p.span, Annotatable::FieldDef(ref sf) => sf.span, Annotatable::Variant(ref v) => v.span, + Annotatable::Crate(ref c) => c.span, } } @@ -84,6 +86,7 @@ impl Annotatable { Annotatable::Param(p) => p.visit_attrs(f), Annotatable::FieldDef(sf) => sf.visit_attrs(f), Annotatable::Variant(v) => v.visit_attrs(f), + Annotatable::Crate(c) => c.visit_attrs(f), } } @@ -102,6 +105,7 @@ impl Annotatable { Annotatable::Param(p) => visitor.visit_param(p), Annotatable::FieldDef(sf) => visitor.visit_field_def(sf), Annotatable::Variant(v) => visitor.visit_variant(v), + Annotatable::Crate(c) => visitor.visit_crate(c), } } @@ -122,7 +126,8 @@ impl Annotatable { | Annotatable::GenericParam(..) | Annotatable::Param(..) | Annotatable::FieldDef(..) - | Annotatable::Variant(..) => panic!("unexpected annotatable"), + | Annotatable::Variant(..) + | Annotatable::Crate(..) => panic!("unexpected annotatable"), } } @@ -220,6 +225,13 @@ impl Annotatable { _ => panic!("expected variant"), } } + + pub fn expect_crate(self) -> ast::Crate { + match self { + Annotatable::Crate(krate) => krate, + _ => panic!("expected krate"), + } + } } /// Result of an expansion that may need to be retried. @@ -419,6 +431,11 @@ pub trait MacResult { fn make_variants(self: Box<Self>) -> Option<SmallVec<[ast::Variant; 1]>> { None } + + fn make_crate(self: Box<Self>) -> Option<ast::Crate> { + // Fn-like macros cannot produce a crate. + unreachable!() + } } macro_rules! make_MacEager { @@ -903,8 +920,25 @@ pub trait ResolverExpand { /// we generated proc macros harnesses, so that we can map /// HIR proc macros items back to their harness items. fn declare_proc_macro(&mut self, id: NodeId); + + /// Tools registered with `#![register_tool]` and used by tool attributes and lints. + fn registered_tools(&self) -> &FxHashSet<Ident>; } +pub trait LintStoreExpand { + fn pre_expansion_lint( + &self, + sess: &Session, + registered_tools: &FxHashSet<Ident>, + node_id: NodeId, + attrs: &[Attribute], + items: &[P<Item>], + name: &str, + ); +} + +type LintStoreExpandDyn<'a> = Option<&'a (dyn LintStoreExpand + 'a)>; + #[derive(Clone, Default)] pub struct ModuleData { /// Path to the module starting from the crate name, like `my_crate::foo::bar`. @@ -939,9 +973,6 @@ pub struct ExpansionData { pub is_trailing_mac: bool, } -type OnExternModLoaded<'a> = - Option<&'a dyn Fn(Ident, Vec<Attribute>, Vec<P<Item>>, Span) -> (Vec<Attribute>, Vec<P<Item>>)>; - /// One of these is made during expansion and incrementally updated as we go; /// when a macro expansion occurs, the resulting nodes have the `backtrace() /// -> expn_data` of their expansion context stored into their span. @@ -956,10 +987,8 @@ pub struct ExtCtxt<'a> { /// (or during eager expansion, but that's a hack). pub force_mode: bool, pub expansions: FxHashMap<Span, Vec<String>>, - /// Called directly after having parsed an external `mod foo;` in expansion. - /// - /// `Ident` is the module name. - pub(super) extern_mod_loaded: OnExternModLoaded<'a>, + /// Used for running pre-expansion lints on freshly loaded modules. + pub(super) lint_store: LintStoreExpandDyn<'a>, /// When we 'expand' an inert attribute, we leave it /// in the AST, but insert it here so that we know /// not to expand it again. @@ -971,14 +1000,14 @@ impl<'a> ExtCtxt<'a> { sess: &'a Session, ecfg: expand::ExpansionConfig<'a>, resolver: &'a mut dyn ResolverExpand, - extern_mod_loaded: OnExternModLoaded<'a>, + lint_store: LintStoreExpandDyn<'a>, ) -> ExtCtxt<'a> { ExtCtxt { sess, ecfg, reduced_recursion_limit: None, resolver, - extern_mod_loaded, + lint_store, root_path: PathBuf::new(), current_expansion: ExpansionData { id: LocalExpnId::ROOT, diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 1b123520961..5fa7ffd554e 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -171,7 +171,7 @@ fn get_features( } if let Some(allowed) = sess.opts.debugging_opts.allow_features.as_ref() { - if allowed.iter().all(|f| name.as_str() != *f) { + if allowed.iter().all(|f| name.as_str() != f) { struct_span_err!( span_handler, mi.span(), @@ -238,7 +238,7 @@ macro_rules! configure { } impl<'a> StripUnconfigured<'a> { - pub fn configure<T: AstLike>(&mut self, mut node: T) -> Option<T> { + pub fn configure<T: AstLike>(&self, mut node: T) -> Option<T> { self.process_cfg_attrs(&mut node); if self.in_cfg(node.attrs()) { self.try_configure_tokens(&mut node); @@ -248,7 +248,7 @@ impl<'a> StripUnconfigured<'a> { } } - fn try_configure_tokens<T: AstLike>(&mut self, node: &mut T) { + fn try_configure_tokens<T: AstLike>(&self, node: &mut T) { if self.config_tokens { if let Some(Some(tokens)) = node.tokens_mut() { let attr_annotated_tokens = tokens.create_token_stream(); @@ -257,10 +257,7 @@ impl<'a> StripUnconfigured<'a> { } } - fn configure_krate_attrs( - &mut self, - mut attrs: Vec<ast::Attribute>, - ) -> Option<Vec<ast::Attribute>> { + fn configure_krate_attrs(&self, mut attrs: Vec<ast::Attribute>) -> Option<Vec<ast::Attribute>> { attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); if self.in_cfg(&attrs) { Some(attrs) } else { None } } @@ -269,7 +266,7 @@ impl<'a> StripUnconfigured<'a> { /// This is only used during the invocation of `derive` proc-macros, /// which require that we cfg-expand their entire input. /// Normal cfg-expansion operates on parsed AST nodes via the `configure` method - fn configure_tokens(&mut self, stream: &AttrAnnotatedTokenStream) -> AttrAnnotatedTokenStream { + fn configure_tokens(&self, stream: &AttrAnnotatedTokenStream) -> AttrAnnotatedTokenStream { fn can_skip(stream: &AttrAnnotatedTokenStream) -> bool { stream.0.iter().all(|(tree, _spacing)| match tree { AttrAnnotatedTokenTree::Attributes(_) => false, @@ -325,12 +322,16 @@ impl<'a> StripUnconfigured<'a> { /// Gives compiler warnings if any `cfg_attr` does not contain any /// attributes and is in the original source code. Gives compiler errors if /// the syntax of any `cfg_attr` is incorrect. - fn process_cfg_attrs<T: AstLike>(&mut self, node: &mut T) { + fn process_cfg_attrs<T: AstLike>(&self, node: &mut T) { node.visit_attrs(|attrs| { attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); }); } + fn process_cfg_attr(&self, attr: Attribute) -> Vec<Attribute> { + if attr.has_name(sym::cfg_attr) { self.expand_cfg_attr(attr, true) } else { vec![attr] } + } + /// Parse and expand a single `cfg_attr` attribute into a list of attributes /// when the configuration predicate is true, or otherwise expand into an /// empty list of attributes. @@ -338,11 +339,7 @@ impl<'a> StripUnconfigured<'a> { /// Gives a compiler warning when the `cfg_attr` contains no attributes and /// is in the original source file. Gives a compiler error if the syntax of /// the attribute is incorrect. - fn process_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> { - if !attr.has_name(sym::cfg_attr) { - return vec![attr]; - } - + crate fn expand_cfg_attr(&self, attr: Attribute, recursive: bool) -> Vec<Attribute> { let (cfg_predicate, expanded_attrs) = match rustc_parse::parse_cfg_attr(&attr, &self.sess.parse_sess) { None => return vec![], @@ -351,78 +348,109 @@ impl<'a> StripUnconfigured<'a> { // Lint on zero attributes in source. if expanded_attrs.is_empty() { - return vec![attr]; + self.sess.parse_sess.buffer_lint( + rustc_lint_defs::builtin::UNUSED_ATTRIBUTES, + attr.span, + ast::CRATE_NODE_ID, + "`#[cfg_attr]` does not expand to any attributes", + ); } if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) { return vec![]; } - // We call `process_cfg_attr` recursively in case there's a - // `cfg_attr` inside of another `cfg_attr`. E.g. - // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. - expanded_attrs - .into_iter() - .flat_map(|(item, span)| { - let orig_tokens = attr.tokens().to_tokenstream(); - - // We are taking an attribute of the form `#[cfg_attr(pred, attr)]` - // and producing an attribute of the form `#[attr]`. We - // have captured tokens for `attr` itself, but we need to - // synthesize tokens for the wrapper `#` and `[]`, which - // we do below. - - // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token - // for `attr` when we expand it to `#[attr]` - let mut orig_trees = orig_tokens.trees(); - let pound_token = match orig_trees.next().unwrap() { - TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token, - _ => panic!("Bad tokens for attribute {:?}", attr), - }; - let pound_span = pound_token.span; - - let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)]; - if attr.style == AttrStyle::Inner { - // For inner attributes, we do the same thing for the `!` in `#![some_attr]` - let bang_token = match orig_trees.next().unwrap() { - TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token, - _ => panic!("Bad tokens for attribute {:?}", attr), - }; - trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone)); - } - // We don't really have a good span to use for the syntheized `[]` - // in `#[attr]`, so just use the span of the `#` token. - let bracket_group = AttrAnnotatedTokenTree::Delimited( - DelimSpan::from_single(pound_span), - DelimToken::Bracket, - item.tokens - .as_ref() - .unwrap_or_else(|| panic!("Missing tokens for {:?}", item)) - .create_token_stream(), - ); - trees.push((bracket_group, Spacing::Alone)); - let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees))); - self.process_cfg_attr(attr::mk_attr_from_item(item, tokens, attr.style, span)) - }) - .collect() + if recursive { + // We call `process_cfg_attr` recursively in case there's a + // `cfg_attr` inside of another `cfg_attr`. E.g. + // `#[cfg_attr(false, cfg_attr(true, some_attr))]`. + expanded_attrs + .into_iter() + .flat_map(|item| self.process_cfg_attr(self.expand_cfg_attr_item(&attr, item))) + .collect() + } else { + expanded_attrs.into_iter().map(|item| self.expand_cfg_attr_item(&attr, item)).collect() + } + } + + fn expand_cfg_attr_item( + &self, + attr: &Attribute, + (item, item_span): (ast::AttrItem, Span), + ) -> Attribute { + let orig_tokens = attr.tokens().to_tokenstream(); + + // We are taking an attribute of the form `#[cfg_attr(pred, attr)]` + // and producing an attribute of the form `#[attr]`. We + // have captured tokens for `attr` itself, but we need to + // synthesize tokens for the wrapper `#` and `[]`, which + // we do below. + + // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token + // for `attr` when we expand it to `#[attr]` + let mut orig_trees = orig_tokens.trees(); + let pound_token = match orig_trees.next().unwrap() { + TokenTree::Token(token @ Token { kind: TokenKind::Pound, .. }) => token, + _ => panic!("Bad tokens for attribute {:?}", attr), + }; + let pound_span = pound_token.span; + + let mut trees = vec![(AttrAnnotatedTokenTree::Token(pound_token), Spacing::Alone)]; + if attr.style == AttrStyle::Inner { + // For inner attributes, we do the same thing for the `!` in `#![some_attr]` + let bang_token = match orig_trees.next().unwrap() { + TokenTree::Token(token @ Token { kind: TokenKind::Not, .. }) => token, + _ => panic!("Bad tokens for attribute {:?}", attr), + }; + trees.push((AttrAnnotatedTokenTree::Token(bang_token), Spacing::Alone)); + } + // We don't really have a good span to use for the syntheized `[]` + // in `#[attr]`, so just use the span of the `#` token. + let bracket_group = AttrAnnotatedTokenTree::Delimited( + DelimSpan::from_single(pound_span), + DelimToken::Bracket, + item.tokens + .as_ref() + .unwrap_or_else(|| panic!("Missing tokens for {:?}", item)) + .create_token_stream(), + ); + trees.push((bracket_group, Spacing::Alone)); + let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees))); + let attr = attr::mk_attr_from_item(item, tokens, attr.style, item_span); + if attr.has_name(sym::crate_type) { + self.sess.parse_sess.buffer_lint( + rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, + attr.span, + ast::CRATE_NODE_ID, + "`crate_type` within an `#![cfg_attr] attribute is deprecated`", + ); + } + if attr.has_name(sym::crate_name) { + self.sess.parse_sess.buffer_lint( + rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, + attr.span, + ast::CRATE_NODE_ID, + "`crate_name` within an `#![cfg_attr] attribute is deprecated`", + ); + } + attr } /// Determines if a node with the given attributes should be included in this configuration. fn in_cfg(&self, attrs: &[Attribute]) -> bool { - attrs.iter().all(|attr| { - if !is_cfg(attr) { + attrs.iter().all(|attr| !is_cfg(attr) || self.cfg_true(attr)) + } + + crate fn cfg_true(&self, attr: &Attribute) -> bool { + let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) { + Ok(meta_item) => meta_item, + Err(mut err) => { + err.emit(); return true; } - let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) { - Ok(meta_item) => meta_item, - Err(mut err) => { - err.emit(); - return true; - } - }; - parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| { - attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features) - }) + }; + parse_cfg(&meta_item, &self.sess).map_or(true, |meta_item| { + attr::cfg_matches(&meta_item, &self.sess.parse_sess, self.features) }) } @@ -444,7 +472,7 @@ impl<'a> StripUnconfigured<'a> { } } - pub fn configure_expr(&mut self, expr: &mut P<ast::Expr>) { + pub fn configure_expr(&self, expr: &mut P<ast::Expr>) { for attr in expr.attrs.iter() { self.maybe_emit_expr_attr_err(attr); } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 89dbd64ed81..9a4daa6d750 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1,6 +1,5 @@ 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}; @@ -12,15 +11,13 @@ 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, Block, Inline, ItemKind, MacArgs, MacCall}; -use rustc_ast::{MacCallStmt, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem}; -use rustc_ast::{NodeId, PatKind, Path, StmtKind, Unsafe}; +use rustc_ast::{AssocItemKind, AstLike, AstLikeWrapper, AttrStyle, ExprKind, ForeignItemKind}; +use rustc_ast::{Inline, ItemKind, MacArgs, MacStmtStyle, MetaItemKind, ModKind, NestedMetaItem}; +use rustc_ast::{NodeId, PatKind, StmtKind, TyKind}; use rustc_ast_pretty::pprust; -use rustc_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_errors::{Applicability, PResult}; use rustc_feature::Features; use rustc_parse::parser::{ AttemptLocalParseRecovery, ForceCollect, Parser, RecoverColon, RecoverComma, @@ -33,8 +30,8 @@ use rustc_session::Limit; use rustc_span::symbol::{sym, Ident}; use rustc_span::{FileName, LocalExpnId, Span}; -use smallvec::{smallvec, SmallVec}; -use std::ops::DerefMut; +use smallvec::SmallVec; +use std::ops::Deref; use std::path::PathBuf; use std::rc::Rc; use std::{iter, mem}; @@ -109,6 +106,10 @@ macro_rules! ast_fragments { } })* + fn make_ast<T: InvocationCollectorNode>(self) -> T::OutputTy { + T::fragment_to_output(self) + } + pub fn mut_visit_with<F: MutVisitor>(&mut self, vis: &mut F) { match self { AstFragment::OptExpr(opt_expr) => { @@ -178,10 +179,10 @@ ast_fragments! { Arms(SmallVec<[ast::Arm; 1]>) { "match arm"; many fn flat_map_arm; fn visit_arm(); fn make_arms; } - Fields(SmallVec<[ast::ExprField; 1]>) { + ExprFields(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]>) { + PatFields(SmallVec<[ast::PatField; 1]>) { "field pattern"; many fn flat_map_pat_field; fn visit_pat_field(); @@ -196,7 +197,7 @@ ast_fragments! { Params(SmallVec<[ast::Param; 1]>) { "function parameter"; many fn flat_map_param; fn visit_param(); fn make_params; } - StructFields(SmallVec<[ast::FieldDef; 1]>) { + FieldDefs(SmallVec<[ast::FieldDef; 1]>) { "field"; many fn flat_map_field_def; fn visit_field_def(); @@ -205,6 +206,7 @@ ast_fragments! { Variants(SmallVec<[ast::Variant; 1]>) { "variant"; many fn flat_map_variant; fn visit_variant(); fn make_variants; } + Crate(ast::Crate) { "crate"; one fn visit_crate; fn visit_crate; fn make_crate; } } pub enum SupportsMacroExpansion { @@ -227,15 +229,14 @@ impl AstFragmentKind { AstFragmentKind::Items | AstFragmentKind::TraitItems | AstFragmentKind::ImplItems - | AstFragmentKind::ForeignItems => { - SupportsMacroExpansion::Yes { supports_inner_attrs: true } - } + | AstFragmentKind::ForeignItems + | AstFragmentKind::Crate => SupportsMacroExpansion::Yes { supports_inner_attrs: true }, AstFragmentKind::Arms - | AstFragmentKind::Fields - | AstFragmentKind::FieldPats + | AstFragmentKind::ExprFields + | AstFragmentKind::PatFields | AstFragmentKind::GenericParams | AstFragmentKind::Params - | AstFragmentKind::StructFields + | AstFragmentKind::FieldDefs | AstFragmentKind::Variants => SupportsMacroExpansion::No, } } @@ -249,11 +250,11 @@ impl AstFragmentKind { AstFragmentKind::Arms => { AstFragment::Arms(items.map(Annotatable::expect_arm).collect()) } - AstFragmentKind::Fields => { - AstFragment::Fields(items.map(Annotatable::expect_expr_field).collect()) + AstFragmentKind::ExprFields => { + AstFragment::ExprFields(items.map(Annotatable::expect_expr_field).collect()) } - AstFragmentKind::FieldPats => { - AstFragment::FieldPats(items.map(Annotatable::expect_pat_field).collect()) + AstFragmentKind::PatFields => { + AstFragment::PatFields(items.map(Annotatable::expect_pat_field).collect()) } AstFragmentKind::GenericParams => { AstFragment::GenericParams(items.map(Annotatable::expect_generic_param).collect()) @@ -261,8 +262,8 @@ impl AstFragmentKind { AstFragmentKind::Params => { AstFragment::Params(items.map(Annotatable::expect_param).collect()) } - AstFragmentKind::StructFields => { - AstFragment::StructFields(items.map(Annotatable::expect_field_def).collect()) + AstFragmentKind::FieldDefs => { + AstFragment::FieldDefs(items.map(Annotatable::expect_field_def).collect()) } AstFragmentKind::Variants => { AstFragment::Variants(items.map(Annotatable::expect_variant).collect()) @@ -288,6 +289,9 @@ impl AstFragmentKind { 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") } @@ -312,10 +316,10 @@ pub enum InvocationKind { pos: usize, item: Annotatable, // Required for resolving derive helper attributes. - derives: Vec<Path>, + derives: Vec<ast::Path>, }, Derive { - path: Path, + path: ast::Path, item: Annotatable, }, } @@ -359,9 +363,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { 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 { + pub fn expand_crate(&mut self, krate: ast::Crate) -> ast::Crate { let file_path = match self.cx.source_map().span_to_filename(krate.span) { FileName::Real(name) => name .into_local_path() @@ -375,52 +377,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { 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::empty(), - 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(); - } - }; + 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 } @@ -708,26 +666,32 @@ impl<'a, 'b> MacroExpander<'a, 'b> { 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( + 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.parse_sess, - &item.into_nonterminal(), - ) - } else { - item.into_tokens(&self.cx.sess.parse_sess) + 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( + &self.cx.sess.parse_sess, + &item.into_nonterminal(), + ) + } + _ => item.into_tokens(&self.cx.sess.parse_sess), }; let attr_item = attr.unwrap_normal_item(); if let MacArgs::Eq(..) = attr_item.args { @@ -781,7 +745,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { if let SyntaxExtensionKind::Derive(..) = ext { self.gate_proc_macro_input(&item); } - let meta = ast::MetaItem { kind: ast::MetaItemKind::Word, span, path }; + let meta = ast::MetaItem { kind: MetaItemKind::Word, span, path }; let items = match expander.expand(self.cx, span, &meta, item) { ExpandResult::Ready(items) => items, ExpandResult::Retry(item) => { @@ -804,7 +768,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { Annotatable::Item(_) | Annotatable::TraitItem(_) | Annotatable::ImplItem(_) - | Annotatable::ForeignItem(_) => return, + | Annotatable::ForeignItem(_) + | Annotatable::Crate(..) => return, Annotatable::Stmt(stmt) => { // Attributes are stable on item statements, // but unstable on all other kinds of statements @@ -842,7 +807,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { 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) + ItemKind::Mod(_, mod_kind) if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _)) => { feature_err( @@ -870,7 +835,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { &mut self, toks: TokenStream, kind: AstFragmentKind, - path: &Path, + path: &ast::Path, span: Span, ) -> AstFragment { let mut parser = self.cx.new_parser_from_tts(toks); @@ -949,19 +914,20 @@ pub fn parse_ast_fragment<'a>( RecoverComma::No, RecoverColon::Yes, )?), + AstFragmentKind::Crate => AstFragment::Crate(this.parse_crate_mod()?), AstFragmentKind::Arms - | AstFragmentKind::Fields - | AstFragmentKind::FieldPats + | AstFragmentKind::ExprFields + | AstFragmentKind::PatFields | AstFragmentKind::GenericParams | AstFragmentKind::Params - | AstFragmentKind::StructFields + | AstFragmentKind::FieldDefs | AstFragmentKind::Variants => panic!("unexpected AST fragment kind"), }) } pub fn ensure_complete_parse<'a>( this: &mut Parser<'a>, - macro_path: &Path, + macro_path: &ast::Path, kind_name: &str, span: Span, ) { @@ -996,6 +962,581 @@ pub fn ensure_complete_parse<'a>( } } +/// Wraps a call to `noop_visit_*` / `noop_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 +/// `noop_visit_*` / `noop_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: AstLike { + type OutputTy = SmallVec<[Self; 1]>; + type AttrsTy: Deref<Target = [ast::Attribute]> = Vec<ast::Attribute>; + const KIND: AstFragmentKind; + fn to_annotatable(self) -> Annotatable; + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy; + fn id(&mut self) -> &mut NodeId; + fn descr() -> &'static str { + unreachable!() + } + fn noop_flat_map<V: MutVisitor>(self, _visitor: &mut V) -> Self::OutputTy { + unreachable!() + } + fn noop_visit<V: MutVisitor>(&mut self, _visitor: &mut V) { + unreachable!() + } + fn is_mac_call(&self) -> bool { + false + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + 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_noop_flat_map( + node: Self, + collector: &mut InvocationCollector<'_, '_>, + noop_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy, + ) -> Result<Self::OutputTy, Self> { + Ok(noop_flat_map(node, collector)) + } +} + +impl InvocationCollectorNode for P<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 id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_item(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, ItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + ItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } + fn wrap_flat_map_node_noop_flat_map( + mut node: Self, + collector: &mut InvocationCollector<'_, '_>, + noop_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy, + ) -> Result<Self::OutputTy, Self> { + if !matches!(node.kind, ItemKind::Mod(..)) { + return Ok(noop_flat_map(node, collector)); + } + + // Work around borrow checker not seeing through `P`'s deref. + let (ident, span, mut attrs) = (node.ident, node.span, mem::take(&mut node.attrs)); + let ItemKind::Mod(_, mod_kind) = &mut 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, + &attrs, + &ecx.current_expansion.module, + ecx.current_expansion.dir_ownership, + *inline, + ); + node.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 { items, inner_span, file_path, dir_path, dir_ownership } = + parse_external_mod( + &ecx.sess, + ident, + span, + &ecx.current_expansion.module, + ecx.current_expansion.dir_ownership, + &mut attrs, + ); + + if let Some(lint_store) = ecx.lint_store { + lint_store.pre_expansion_lint( + ecx.sess, + ecx.resolver.registered_tools(), + ecx.current_expansion.lint_node_id, + &attrs, + &items, + ident.name.as_str(), + ); + } + + *mod_kind = ModKind::Loaded(items, Inline::No, inner_span); + node.attrs = attrs; + 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(noop_flat_map(node, collector)); + + collector.cx.current_expansion.dir_ownership = orig_dir_ownership; + collector.cx.current_expansion.module = orig_module; + res + } +} + +struct TraitItemTag; +impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, TraitItemTag> { + type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>; + const KIND: AstFragmentKind = AstFragmentKind::TraitItems; + fn to_annotatable(self) -> Annotatable { + Annotatable::TraitItem(self.wrapped) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_trait_items() + } + fn id(&mut self) -> &mut NodeId { + &mut self.wrapped.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_assoc_item(self.wrapped, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let item = self.wrapped.into_inner(); + match item.kind { + AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + +struct ImplItemTag; +impl InvocationCollectorNode for AstLikeWrapper<P<ast::AssocItem>, ImplItemTag> { + type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>; + const KIND: AstFragmentKind = AstFragmentKind::ImplItems; + fn to_annotatable(self) -> Annotatable { + Annotatable::ImplItem(self.wrapped) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_impl_items() + } + fn id(&mut self) -> &mut NodeId { + &mut self.wrapped.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_assoc_item(self.wrapped, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.wrapped.kind, AssocItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let item = self.wrapped.into_inner(); + match item.kind { + AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + +impl InvocationCollectorNode for P<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 id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_foreign_item(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, ForeignItemKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + ForeignItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + +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 id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_variant(self, visitor) + } +} + +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 id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_field_def(self, visitor) + } +} + +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 id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_pat_field(self, visitor) + } +} + +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 id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_expr_field(self, visitor) + } +} + +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 id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_param(self, visitor) + } +} + +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 id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_generic_param(self, visitor) + } +} + +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 id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_arm(self, visitor) + } +} + +impl InvocationCollectorNode for ast::Stmt { + type AttrsTy = ast::AttrVec; + const KIND: AstFragmentKind = AstFragmentKind::Stmts; + fn to_annotatable(self) -> Annotatable { + Annotatable::Stmt(P(self)) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_stmts() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy { + noop_flat_map_stmt(self, visitor) + } + 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::Local(..) | StmtKind::Empty => false, + } + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, 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.into_inner(); + (style == MacStmtStyle::Semicolon, mac, attrs) + } + StmtKind::Item(item) => match item.into_inner() { + ast::Item { kind: ItemKind::MacCall(mac), attrs, .. } => { + (mac.args.need_semicolon(), mac, attrs.into()) + } + _ => unreachable!(), + }, + StmtKind::Semi(expr) => match expr.into_inner() { + 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 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()); + } + } + } +} + +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 id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) { + noop_visit_crate(self, visitor) + } +} + +impl InvocationCollectorNode for P<ast::Ty> { + type OutputTy = P<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 id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) { + noop_visit_ty(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, ast::TyKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + TyKind::MacCall(mac) => (mac, Vec::new(), AddSemicolon::No), + _ => unreachable!(), + } + } +} + +impl InvocationCollectorNode for P<ast::Pat> { + type OutputTy = P<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 id(&mut self) -> &mut NodeId { + &mut self.id + } + fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) { + noop_visit_pat(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, PatKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + PatKind::MacCall(mac) => (mac, Vec::new(), AddSemicolon::No), + _ => unreachable!(), + } + } +} + +impl InvocationCollectorNode for P<ast::Expr> { + type OutputTy = P<ast::Expr>; + type AttrsTy = ast::AttrVec; + const KIND: AstFragmentKind = AstFragmentKind::Expr; + fn to_annotatable(self) -> Annotatable { + Annotatable::Expr(self) + } + fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy { + fragment.make_expr() + } + fn id(&mut self) -> &mut NodeId { + &mut self.id + } + fn descr() -> &'static str { + "an expression" + } + fn noop_visit<V: MutVisitor>(&mut self, visitor: &mut V) { + noop_visit_expr(self, visitor) + } + fn is_mac_call(&self) -> bool { + matches!(self.kind, ExprKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.into_inner(); + match node.kind { + ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No), + _ => unreachable!(), + } + } +} + +struct OptExprTag; +impl InvocationCollectorNode for AstLikeWrapper<P<ast::Expr>, OptExprTag> { + type OutputTy = Option<P<ast::Expr>>; + type AttrsTy = ast::AttrVec; + 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 id(&mut self) -> &mut NodeId { + &mut self.wrapped.id + } + fn noop_flat_map<V: MutVisitor>(mut self, visitor: &mut V) -> Self::OutputTy { + noop_visit_expr(&mut self.wrapped, visitor); + Some(self.wrapped) + } + fn is_mac_call(&self) -> bool { + matches!(self.wrapped.kind, ast::ExprKind::MacCall(..)) + } + fn take_mac_call(self) -> (ast::MacCall, Self::AttrsTy, AddSemicolon) { + let node = self.wrapped.into_inner(); + 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); + } +} + struct InvocationCollector<'a, 'b> { cx: &'a mut ExtCtxt<'b>, cfg: StripUnconfigured<'a>, @@ -1031,7 +1572,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect_attr( &mut self, - (attr, pos, derives): (ast::Attribute, usize, Vec<Path>), + (attr, pos, derives): (ast::Attribute, usize, Vec<ast::Path>), item: Annotatable, kind: AstFragmentKind, ) -> AstFragment { @@ -1042,18 +1583,33 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { /// 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, + &self, item: &mut impl AstLike, - ) -> Option<(ast::Attribute, usize, Vec<Path>)> { + ) -> 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 !attr.is_doc_comment() && !self.cx.expanded_inert_attrs.is_marked(attr) { + let name = attr.ident().map(|ident| ident.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.map_or(false, 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 = attrs - .iter() - .position(|a| !self.cx.expanded_inert_attrs.is_marked(a) && !is_builtin_attr(a)) - .map(|attr_pos| { - let attr = attrs.remove(attr_pos); - let following_derives = attrs[attr_pos..] + 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()) @@ -1067,52 +1623,18 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { }) .collect(); - (attr, attr_pos, following_derives) - }) + (attr, pos, following_derives) + } + _ => return, + }); }); attr } - fn take_stmt_bang( - &mut self, - stmt: ast::Stmt, - ) -> Result<(bool, MacCall, Vec<ast::Attribute>), ast::Stmt> { - match stmt.kind { - StmtKind::MacCall(mac) => { - let MacCallStmt { mac, style, attrs, .. } = mac.into_inner(); - Ok((style == MacStmtStyle::Semicolon, mac, attrs.into())) - } - StmtKind::Item(item) if matches!(item.kind, ItemKind::MacCall(..)) => { - match item.into_inner() { - ast::Item { kind: ItemKind::MacCall(mac), attrs, .. } => { - Ok((mac.args.need_semicolon(), mac, attrs)) - } - _ => unreachable!(), - } - } - StmtKind::Semi(expr) if matches!(expr.kind, ast::ExprKind::MacCall(..)) => { - match expr.into_inner() { - ast::Expr { kind: ast::ExprKind::MacCall(mac), attrs, .. } => { - Ok((mac.args.need_semicolon(), mac, attrs.into())) - } - _ => unreachable!(), - } - } - StmtKind::Local(..) | StmtKind::Empty | StmtKind::Item(..) | StmtKind::Semi(..) => { - Err(stmt) - } - StmtKind::Expr(..) => unreachable!(), - } - } - - 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(&self, attrs: &[ast::Attribute], call: &MacCall) { + fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) { let features = self.cx.ecfg.features.unwrap(); let mut attrs = attrs.iter().peekable(); let mut span: Option<Span> = None; @@ -1155,483 +1677,231 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { } } } -} -/// Wraps a call to `noop_visit_*` / `noop_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 -/// `noop_visit_*` / `noop_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; + fn expand_cfg_true( + &mut self, + node: &mut impl AstLike, + attr: ast::Attribute, + pos: usize, + ) -> bool { + let res = self.cfg.cfg_true(&attr); + if res { + // FIXME: `cfg(TRUE)` attributes do not currently remove themselves during expansion, + // and some tools like rustdoc and clippy rely on that. Find a way to remove them + // while keeping the tools working. + self.cx.expanded_inert_attrs.mark(&attr); + node.visit_attrs(|attrs| attrs.insert(pos, attr)); } - let ret = ($closure)(); - $self.cx.current_expansion.lint_node_id = old_id; - ret - }}; -} - -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(); - } + res + } - if let ast::ExprKind::MacCall(mac) = expr.kind { - self.check_attributes(&expr.attrs, &mac); - self.collect_bang(mac, AstFragmentKind::Expr).make_expr().into_inner() - } else { - assign_id!(self, &mut expr.id, || { - ensure_sufficient_stack(|| noop_visit_expr(&mut expr, self)); - }); - expr - } + fn expand_cfg_attr(&self, node: &mut impl AstLike, attr: ast::Attribute, pos: usize) { + node.visit_attrs(|attrs| { + attrs.splice(pos..pos, self.cfg.expand_cfg_attr(attr, false)); }); } - 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(); + 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_or_empty() { + sym::cfg => { + if self.expand_cfg_true(&mut node, attr, pos) { + continue; + } + Default::default() + } + 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 => { + match Node::wrap_flat_map_node_noop_flat_map(node, self, |mut node, this| { + assign_id!(this, node.id(), || node.noop_flat_map(this)) + }) { + Ok(output) => output, + Err(returned_node) => { + node = returned_node; + continue; + } + } + } + }; } - - assign_id!(self, &mut arm.id, || 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(); + fn visit_node<Node: InvocationCollectorNode<OutputTy = Node> + DummyAstNode>( + &mut self, + node: &mut Node, + ) { + loop { + return match self.take_first_attr(node) { + Some((attr, pos, derives)) => match attr.name_or_empty() { + sym::cfg => { + let span = attr.span; + if self.expand_cfg_true(node, attr, pos) { + continue; + } + let msg = + format!("removing {} is not supported in this position", Node::descr()); + self.cx.span_err(span, &msg); + continue; + } + sym::cfg_attr => { + self.expand_cfg_attr(node, attr, pos); + continue; + } + _ => visit_clobber(node, |node| { + self.collect_attr((attr, pos, derives), node.to_annotatable(), Node::KIND) + .make_ast::<Node>() + }), + }, + None if node.is_mac_call() => { + visit_clobber(node, |node| { + // Do not clobber unless it's actually a macro (uncommon case). + let (mac, attrs, _) = node.take_mac_call(); + self.check_attributes(&attrs, &mac); + self.collect_bang(mac, Node::KIND).make_ast::<Node>() + }) + } + None => { + assign_id!(self, node.id(), || node.noop_visit(self)) + } + }; } - - assign_id!(self, &mut field.id, || 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(); - } - - assign_id!(self, &mut fp.id, || noop_flat_map_pat_field(fp, self)) +impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> { + fn flat_map_item(&mut self, node: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> { + self.flat_map_node(node) } - 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(); - } - - assign_id!(self, &mut p.id, || noop_flat_map_param(p, self)) + fn flat_map_trait_item(&mut self, node: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> { + self.flat_map_node(AstLikeWrapper::new(node, TraitItemTag)) } - 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(); - } - - assign_id!(self, &mut sf.id, || noop_flat_map_field_def(sf, self)) + fn flat_map_impl_item(&mut self, node: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> { + self.flat_map_node(AstLikeWrapper::new(node, ImplItemTag)) } - 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(); - } + fn flat_map_foreign_item( + &mut self, + node: P<ast::ForeignItem>, + ) -> SmallVec<[P<ast::ForeignItem>; 1]> { + self.flat_map_node(node) + } - assign_id!(self, &mut variant.id, || noop_flat_map_variant(variant, self)) + fn flat_map_variant(&mut self, node: ast::Variant) -> SmallVec<[ast::Variant; 1]> { + self.flat_map_node(node) } - 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); + fn flat_map_field_def(&mut self, node: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> { + self.flat_map_node(node) + } - return self - .collect_attr(attr, Annotatable::Expr(P(expr)), AstFragmentKind::OptExpr) - .make_opt_expr() - .map(|expr| expr.into_inner()); - } + fn flat_map_pat_field(&mut self, node: ast::PatField) -> SmallVec<[ast::PatField; 1]> { + self.flat_map_node(node) + } - if let ast::ExprKind::MacCall(mac) = expr.kind { - self.check_attributes(&expr.attrs, &mac); - self.collect_bang(mac, AstFragmentKind::OptExpr) - .make_opt_expr() - .map(|expr| expr.into_inner()) - } else { - assign_id!(self, &mut expr.id, || { - Some({ - noop_visit_expr(&mut expr, self); - expr - }) - }) - } - }) + fn flat_map_expr_field(&mut self, node: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> { + self.flat_map_node(node) } - fn visit_pat(&mut self, pat: &mut P<ast::Pat>) { - match pat.kind { - PatKind::MacCall(_) => {} - _ => return noop_visit_pat(pat, self), - } + fn flat_map_param(&mut self, node: ast::Param) -> SmallVec<[ast::Param; 1]> { + self.flat_map_node(node) + } - visit_clobber(pat, |mut pat| match mem::replace(&mut pat.kind, PatKind::Wild) { - PatKind::MacCall(mac) => self.collect_bang(mac, AstFragmentKind::Pat).make_pat(), - _ => unreachable!(), - }); + fn flat_map_generic_param( + &mut self, + node: ast::GenericParam, + ) -> SmallVec<[ast::GenericParam; 1]> { + self.flat_map_node(node) } - fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { - let mut stmt = configure!(self, stmt); + fn flat_map_arm(&mut self, node: ast::Arm) -> SmallVec<[ast::Arm; 1]> { + self.flat_map_node(node) + } - // 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. + fn flat_map_stmt(&mut self, mut node: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { // FIXME: invocations in semicolon-less expressions positions are expanded as expressions, // changing that requires some compatibility measures. - let mut stmt = 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(); - } - - match self.take_stmt_bang(stmt) { - Ok((add_semicolon, mac, attrs)) => { - self.check_attributes(&attrs, &mac); - let mut stmts = self.collect_bang(mac, 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 add_semicolon { - if let Some(stmt) = stmts.pop() { - stmts.push(stmt.add_trailing_semicolon()); - } - } - - return stmts; + 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 + // traiing 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 = noop_flat_map_stmt(node, self); + self.cx.current_expansion.is_trailing_mac = false; + res } - Err(stmt) => stmt, - } - } else { - stmt - }; - - // 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 - // traiing 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. - let res = match &stmt.kind { - StmtKind::Expr(expr) - if matches!(**expr, ast::Expr { kind: ast::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 - noop_flat_map_stmt(stmt, self) - } - _ => assign_id!(self, &mut stmt.id, || noop_flat_map_stmt(stmt, self)), - }; - self.cx.current_expansion.is_trailing_mac = false; - res - } - - 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(); + _ => assign_id!(self, &mut node.id, || noop_flat_map_stmt(node, self)), + }; } - 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(ref mac) => { - self.check_attributes(&attrs, &mac); - item.attrs = attrs; - item.and_then(|item| match item.kind { - ItemKind::MacCall(mac) => { - self.collect_bang(mac, AstFragmentKind::Items).make_items() - } - _ => unreachable!(), - }) - } - ast::ItemKind::Mod(_, ref mut mod_kind) if ident != Ident::empty() => { - 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 = assign_id!(self, &mut item.id, || 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; - // The crate root is special - don't assign an ID to it. - if !(matches!(item.kind, ast::ItemKind::Mod(..)) && ident == Ident::empty()) { - assign_id!(self, &mut item.id, || noop_flat_map_item(item, self)) - } else { - noop_flat_map_item(item, self) - } - } - } + self.flat_map_node(node) } - 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(ref mac) => { - self.check_attributes(&item.attrs, &mac); - item.and_then(|item| match item.kind { - ast::AssocItemKind::MacCall(mac) => { - self.collect_bang(mac, AstFragmentKind::TraitItems).make_trait_items() - } - _ => unreachable!(), - }) - } - _ => { - assign_id!(self, &mut item.id, || noop_flat_map_assoc_item(item, self)) - } - } + fn visit_crate(&mut self, node: &mut ast::Crate) { + self.visit_node(node) } - 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(ref mac) => { - self.check_attributes(&item.attrs, &mac); - item.and_then(|item| match item.kind { - ast::AssocItemKind::MacCall(mac) => { - self.collect_bang(mac, AstFragmentKind::ImplItems).make_impl_items() - } - _ => unreachable!(), - }) - } - _ => { - assign_id!(self, &mut item.id, || noop_flat_map_assoc_item(item, self)) - } - } + fn visit_ty(&mut self, node: &mut P<ast::Ty>) { + self.visit_node(node) } - 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, AstFragmentKind::Ty).make_ty(), - _ => unreachable!(), - }); + fn visit_pat(&mut self, node: &mut P<ast::Pat>) { + self.visit_node(node) } - 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(ref mac) => { - self.check_attributes(&foreign_item.attrs, &mac); - foreign_item.and_then(|item| match item.kind { - ast::ForeignItemKind::MacCall(mac) => { - self.collect_bang(mac, AstFragmentKind::ForeignItems).make_foreign_items() - } - _ => unreachable!(), - }) - } - _ => { - assign_id!(self, &mut foreign_item.id, || noop_flat_map_foreign_item( - foreign_item, - self - )) - } + fn visit_expr(&mut self, node: &mut P<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); } + self.visit_node(node) } - 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(); - } + fn filter_map_expr(&mut self, node: P<ast::Expr>) -> Option<P<ast::Expr>> { + self.flat_map_node(AstLikeWrapper::new(node, OptExprTag)) + } - assign_id!(self, &mut param.id, || noop_flat_map_generic_param(param, self)) + fn visit_block(&mut self, node: &mut P<ast::Block>) { + let orig_dir_ownership = mem::replace( + &mut self.cx.current_expansion.dir_ownership, + DirOwnership::UnownedViaBlock, + ); + noop_visit_block(node, self); + self.cx.current_expansion.dir_ownership = orig_dir_ownership; } - fn visit_id(&mut self, id: &mut ast::NodeId) { + 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 { diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index 521ca2135c6..dfc07da9169 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -1,9 +1,8 @@ +#![feature(associated_type_bounds)] +#![feature(associated_type_defaults)] #![feature(crate_visibility_modifier)] #![feature(decl_macro)] -#![feature(destructuring_assignment)] -#![feature(format_args_capture)] #![feature(if_let_guard)] -#![feature(iter_zip)] #![feature(let_else)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_internals)] diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index a7434d73abe..dd82add08dd 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -584,9 +584,7 @@ fn inner_parse_loop<'root, 'tt>( // // At the beginning of the loop, if we reach the end of the delimited submatcher, // we pop the stack to backtrack out of the descent. - seq - @ - (TokenTree::Delimited(..) + seq @ (TokenTree::Delimited(..) | TokenTree::Token(Token { kind: DocComment(..), .. })) => { let lower_elts = mem::replace(&mut item.top_elts, Tt(seq)); let idx = item.idx; diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index f8491654f39..8065911afb9 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -253,7 +253,7 @@ fn generic_extension<'cx>( for (i, lhs) in lhses.iter().enumerate() { // try each arm's matchers let lhs_tt = match *lhs { - mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..], + mbe::TokenTree::Delimited(_, ref delim) => &delim.tts, _ => cx.span_bug(sp, "malformed macro lhs"), }; @@ -353,7 +353,7 @@ fn generic_extension<'cx>( for lhs in lhses { // try each arm's matchers let lhs_tt = match *lhs { - mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..], + mbe::TokenTree::Delimited(_, ref delim) => &delim.tts, _ => continue, }; if let Success(_) = @@ -677,11 +677,11 @@ impl FirstSets { first.replace_with(tt.clone()); } TokenTree::Delimited(span, ref delimited) => { - build_recur(sets, &delimited.tts[..]); + build_recur(sets, &delimited.tts); first.replace_with(delimited.open_tt(span)); } TokenTree::Sequence(sp, ref seq_rep) => { - let subfirst = build_recur(sets, &seq_rep.tts[..]); + let subfirst = build_recur(sets, &seq_rep.tts); match sets.first.entry(sp.entire()) { Entry::Vacant(vac) => { @@ -748,7 +748,7 @@ impl FirstSets { let subfirst = match self.first.get(&sp.entire()) { Some(&Some(ref subfirst)) => subfirst, Some(&None) => { - subfirst_owned = self.first(&seq_rep.tts[..]); + subfirst_owned = self.first(&seq_rep.tts); &subfirst_owned } None => { @@ -1027,6 +1027,24 @@ fn check_matcher_core( ), ); err.span_label(sp, format!("not allowed after `{}` fragments", kind)); + + if kind == NonterminalKind::PatWithOr + && sess.edition == Edition::Edition2021 + && next_token.is_token(&BinOp(token::BinOpToken::Or)) + { + let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( + span, + name, + Some(NonterminalKind::PatParam { inferred: false }), + )); + err.span_suggestion( + span, + &format!("try a `pat_param` fragment specifier instead"), + suggestion, + Applicability::MaybeIncorrect, + ); + } + let msg = "allowed there are: "; match possible { &[] => {} diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 88e1623012b..01a7f726617 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -175,12 +175,12 @@ pub(super) fn transcribe<'a>( )); } - LockstepIterSize::Contradiction(ref msg) => { + LockstepIterSize::Contradiction(msg) => { // FIXME: this really ought to be caught at macro definition time... It // happens when two meta-variables are used in the same repetition in a // sequence, but they come from different sequence matchers and repeat // different amounts. - return Err(cx.struct_span_err(seq.span(), &msg[..])); + return Err(cx.struct_span_err(seq.span(), &msg)); } LockstepIterSize::Constraint(len, _) => { diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs index 1c0b2a9b487..e9532dbe2ce 100644 --- a/compiler/rustc_expand/src/module.rs +++ b/compiler/rustc_expand/src/module.rs @@ -103,10 +103,10 @@ crate fn mod_dir_path( if let DirOwnership::Owned { relative } = &mut dir_ownership { if let Some(ident) = relative.take() { // Remove the relative offset. - dir_path.push(&*ident.as_str()); + dir_path.push(ident.as_str()); } } - dir_path.push(&*ident.as_str()); + dir_path.push(ident.as_str()); (dir_path, dir_ownership) } @@ -170,8 +170,8 @@ fn mod_file_path_from_attr( ) -> Option<PathBuf> { // Extract path string from first `#[path = "path_string"]` attribute. let first_path = attrs.iter().find(|at| at.has_name(sym::path))?; - let path_string = match first_path.value_str() { - Some(s) => s.as_str(), + let path_sym = match first_path.value_str() { + Some(s) => s, None => { // This check is here mainly to catch attempting to use a macro, // such as #[path = concat!(...)]. This isn't currently supported @@ -189,14 +189,16 @@ fn mod_file_path_from_attr( } }; + let path_str = path_sym.as_str(); + // On windows, the base path might have the form // `\\?\foo\bar` in which case it does not tolerate // mixed `/` and `\` separators, so canonicalize // `/` to `\`. #[cfg(windows)] - let path_string = path_string.replace("/", "\\"); + let path_str = path_str.replace("/", "\\"); - Some(dir_path.join(&*path_string)) + Some(dir_path.join(path_str)) } /// Returns a path to a module. diff --git a/compiler/rustc_expand/src/parse/tests.rs b/compiler/rustc_expand/src/parse/tests.rs index 6402a81e7c1..4a8236b2cf3 100644 --- a/compiler/rustc_expand/src/parse/tests.rs +++ b/compiler/rustc_expand/src/parse/tests.rs @@ -65,24 +65,33 @@ fn string_to_tts_macro() { let tts: &[TokenTree] = &tts[..]; match tts { - [TokenTree::Token(Token { kind: token::Ident(name_macro_rules, false), .. }), TokenTree::Token(Token { kind: token::Not, .. }), TokenTree::Token(Token { kind: token::Ident(name_zip, false), .. }), TokenTree::Delimited(_, macro_delim, macro_tts)] - if name_macro_rules == &kw::MacroRules && name_zip.as_str() == "zip" => - { + [ + TokenTree::Token(Token { kind: token::Ident(name_macro_rules, false), .. }), + TokenTree::Token(Token { kind: token::Not, .. }), + TokenTree::Token(Token { kind: token::Ident(name_zip, false), .. }), + TokenTree::Delimited(_, macro_delim, macro_tts), + ] if name_macro_rules == &kw::MacroRules && name_zip.as_str() == "zip" => { let tts = ¯o_tts.trees().collect::<Vec<_>>(); match &tts[..] { - [TokenTree::Delimited(_, first_delim, first_tts), TokenTree::Token(Token { kind: token::FatArrow, .. }), TokenTree::Delimited(_, second_delim, second_tts)] - if macro_delim == &token::Paren => - { + [ + TokenTree::Delimited(_, first_delim, first_tts), + TokenTree::Token(Token { kind: token::FatArrow, .. }), + TokenTree::Delimited(_, second_delim, second_tts), + ] if macro_delim == &token::Paren => { let tts = &first_tts.trees().collect::<Vec<_>>(); match &tts[..] { - [TokenTree::Token(Token { kind: token::Dollar, .. }), TokenTree::Token(Token { kind: token::Ident(name, false), .. })] - if first_delim == &token::Paren && name.as_str() == "a" => {} + [ + TokenTree::Token(Token { kind: token::Dollar, .. }), + TokenTree::Token(Token { kind: token::Ident(name, false), .. }), + ] if first_delim == &token::Paren && name.as_str() == "a" => {} _ => panic!("value 3: {:?} {:?}", first_delim, first_tts), } let tts = &second_tts.trees().collect::<Vec<_>>(); match &tts[..] { - [TokenTree::Token(Token { kind: token::Dollar, .. }), TokenTree::Token(Token { kind: token::Ident(name, false), .. })] - if second_delim == &token::Paren && name.as_str() == "a" => {} + [ + TokenTree::Token(Token { kind: token::Dollar, .. }), + TokenTree::Token(Token { kind: token::Ident(name, false), .. }), + ] if second_delim == &token::Paren && name.as_str() == "a" => {} _ => panic!("value 4: {:?} {:?}", second_delim, second_tts), } } diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 12b6bc7bbe7..af593e92634 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -46,6 +46,13 @@ pub fn placeholder( || P(ast::Pat { id, kind: ast::PatKind::MacCall(mac_placeholder()), span, tokens: None }); match kind { + AstFragmentKind::Crate => AstFragment::Crate(ast::Crate { + attrs: Default::default(), + items: Default::default(), + span, + id, + is_placeholder: true, + }), AstFragmentKind::Expr => AstFragment::Expr(expr_placeholder()), AstFragmentKind::OptExpr => AstFragment::OptExpr(Some(expr_placeholder())), AstFragmentKind::Items => AstFragment::Items(smallvec![P(ast::Item { @@ -116,7 +123,7 @@ pub fn placeholder( span, is_placeholder: true, }]), - AstFragmentKind::Fields => AstFragment::Fields(smallvec![ast::ExprField { + AstFragmentKind::ExprFields => AstFragment::ExprFields(smallvec![ast::ExprField { attrs: Default::default(), expr: expr_placeholder(), id, @@ -125,7 +132,7 @@ pub fn placeholder( span, is_placeholder: true, }]), - AstFragmentKind::FieldPats => AstFragment::FieldPats(smallvec![ast::PatField { + AstFragmentKind::PatFields => AstFragment::PatFields(smallvec![ast::PatField { attrs: Default::default(), id, ident, @@ -152,7 +159,7 @@ pub fn placeholder( ty: ty(), is_placeholder: true, }]), - AstFragmentKind::StructFields => AstFragment::StructFields(smallvec![ast::FieldDef { + AstFragmentKind::FieldDefs => AstFragment::FieldDefs(smallvec![ast::FieldDef { attrs: Default::default(), id, ident: None, @@ -354,4 +361,12 @@ impl MutVisitor for PlaceholderExpander { _ => noop_visit_ty(ty, self), } } + + fn visit_crate(&mut self, krate: &mut ast::Crate) { + if krate.is_placeholder { + *krate = self.remove(krate.id).make_crate(); + } else { + noop_visit_crate(krate, self) + } + } } diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs index 3f84979ac05..42c17a60a5d 100644 --- a/compiler/rustc_expand/src/proc_macro.rs +++ b/compiler/rustc_expand/src/proc_macro.rs @@ -24,8 +24,9 @@ impl base::ProcMacro for BangProcMacro { span: Span, input: TokenStream, ) -> Result<TokenStream, ErrorReported> { + let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; let server = proc_macro_server::Rustc::new(ecx); - self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace).map_err(|e| { + self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace).map_err(|e| { let mut err = ecx.struct_span_err(span, "proc macro panicked"); if let Some(s) = e.as_str() { err.help(&format!("message: {}", s)); @@ -48,9 +49,10 @@ impl base::AttrProcMacro for AttrProcMacro { annotation: TokenStream, annotated: TokenStream, ) -> Result<TokenStream, ErrorReported> { + let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; let server = proc_macro_server::Rustc::new(ecx); self.client - .run(&EXEC_STRATEGY, server, annotation, annotated, ecx.ecfg.proc_macro_backtrace) + .run(&EXEC_STRATEGY, server, annotation, annotated, proc_macro_backtrace) .map_err(|e| { let mut err = ecx.struct_span_err(span, "custom attribute panicked"); if let Some(s) = e.as_str() { @@ -97,19 +99,19 @@ impl MultiItemModifier for ProcMacroDerive { nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::No) }; + let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace; let server = proc_macro_server::Rustc::new(ecx); - let stream = - match self.client.run(&EXEC_STRATEGY, server, input, ecx.ecfg.proc_macro_backtrace) { - Ok(stream) => stream, - Err(e) => { - let mut err = ecx.struct_span_err(span, "proc-macro derive panicked"); - if let Some(s) = e.as_str() { - err.help(&format!("message: {}", s)); - } - err.emit(); - return ExpandResult::Ready(vec![]); + let stream = match self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace) { + Ok(stream) => stream, + Err(e) => { + let mut err = ecx.struct_span_err(span, "proc-macro derive panicked"); + if let Some(s) = e.as_str() { + err.help(&format!("message: {}", s)); } - }; + err.emit(); + return ExpandResult::Ready(vec![]); + } + }; let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count(); let mut parser = diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 5cb97198765..efbe0b65715 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -1,4 +1,4 @@ -use crate::base::{ExtCtxt, ResolverExpand}; +use crate::base::ExtCtxt; use rustc_ast as ast; use rustc_ast::token::{self, Nonterminal, NtIdent}; @@ -7,7 +7,7 @@ use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing} use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; -use rustc_errors::Diagnostic; +use rustc_errors::{Diagnostic, PResult}; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; use rustc_lint_defs::BuiltinLintDiagnostics; use rustc_parse::lexer::nfc_normalize; @@ -53,11 +53,11 @@ impl ToInternal<token::DelimToken> for Delimiter { } } -impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_>)> +impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_, '_>)> for TokenTree<Group, Punct, Ident, Literal> { fn from_internal( - ((tree, spacing), stack, rustc): (TreeAndSpacing, &mut Vec<Self>, &mut Rustc<'_>), + ((tree, spacing), stack, rustc): (TreeAndSpacing, &mut Vec<Self>, &mut Rustc<'_, '_>), ) -> Self { use rustc_ast::token::*; @@ -146,10 +146,10 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_>)> SingleQuote => op!('\''), Ident(name, false) if name == kw::DollarCrate => tt!(Ident::dollar_crate()), - Ident(name, is_raw) => tt!(Ident::new(rustc.sess, name, is_raw)), + Ident(name, is_raw) => tt!(Ident::new(rustc.sess(), name, is_raw)), Lifetime(name) => { let ident = symbol::Ident::new(name, span).without_first_quote(); - stack.push(tt!(Ident::new(rustc.sess, ident.name, false))); + stack.push(tt!(Ident::new(rustc.sess(), ident.name, false))); tt!(Punct::new('\'', true)) } Literal(lit) => tt!(Literal { lit }), @@ -158,7 +158,7 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_>)> for ch in data.as_str().chars() { escaped.extend(ch.escape_debug()); } - let stream = vec![ + let stream = [ Ident(sym::doc, false), Eq, TokenKind::lit(token::Str, Symbol::intern(&escaped), None), @@ -181,15 +181,15 @@ impl FromInternal<(TreeAndSpacing, &'_ mut Vec<Self>, &mut Rustc<'_>)> Interpolated(nt) if let Some((name, is_raw)) = ident_name_compatibility_hack(&nt, span, rustc) => { - TokenTree::Ident(Ident::new(rustc.sess, name.name, is_raw, name.span)) + TokenTree::Ident(Ident::new(rustc.sess(), name.name, is_raw, name.span)) } Interpolated(nt) => { - let stream = nt_to_tokenstream(&nt, rustc.sess, CanSynthesizeMissingTokens::No); + let stream = nt_to_tokenstream(&nt, rustc.sess(), CanSynthesizeMissingTokens::No); TokenTree::Group(Group { delimiter: Delimiter::None, stream, span: DelimSpan::from_single(span), - flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess), + flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess()), }) } @@ -221,7 +221,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> { let integer = TokenKind::lit(token::Integer, symbol, suffix); let a = tokenstream::TokenTree::token(minus, span); let b = tokenstream::TokenTree::token(integer, span); - return vec![a, b].into_iter().collect(); + return [a, b].into_iter().collect(); } TokenTree::Literal(self::Literal { lit: token::Lit { kind: token::Float, symbol, suffix }, @@ -232,7 +232,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> { let float = TokenKind::lit(token::Float, symbol, suffix); let a = tokenstream::TokenTree::token(minus, span); let b = tokenstream::TokenTree::token(float, span); - return vec![a, b].into_iter().collect(); + return [a, b].into_iter().collect(); } TokenTree::Literal(self::Literal { lit, span }) => { return tokenstream::TokenTree::token(Literal(lit), span).into(); @@ -273,7 +273,7 @@ impl ToInternal<TokenStream> for TokenTree<Group, Punct, Ident, Literal> { impl ToInternal<rustc_errors::Level> for Level { fn to_internal(self) -> rustc_errors::Level { match self { - Level::Error => rustc_errors::Level::Error, + Level::Error => rustc_errors::Level::Error { lint: false }, Level::Warning => rustc_errors::Level::Warning, Level::Note => rustc_errors::Level::Note, Level::Help => rustc_errors::Level::Help, @@ -331,9 +331,9 @@ pub struct Ident { impl Ident { fn new(sess: &ParseSess, sym: Symbol, is_raw: bool, span: Span) -> Ident { - let sym = nfc_normalize(&sym.as_str()); + let sym = nfc_normalize(sym.as_str()); let string = sym.as_str(); - if !rustc_lexer::is_ident(&string) { + if !rustc_lexer::is_ident(string) { panic!("`{:?}` is not a valid identifier", string) } if is_raw && !sym.can_be_raw() { @@ -355,38 +355,38 @@ pub struct Literal { span: Span, } -pub(crate) struct Rustc<'a> { - resolver: &'a dyn ResolverExpand, - sess: &'a ParseSess, +pub(crate) struct Rustc<'a, 'b> { + ecx: &'a mut ExtCtxt<'b>, def_site: Span, call_site: Span, mixed_site: Span, - span_debug: bool, krate: CrateNum, rebased_spans: FxHashMap<usize, Span>, } -impl<'a> Rustc<'a> { - pub fn new(cx: &'a ExtCtxt<'_>) -> Self { - let expn_data = cx.current_expansion.id.expn_data(); +impl<'a, 'b> Rustc<'a, 'b> { + pub fn new(ecx: &'a mut ExtCtxt<'b>) -> Self { + let expn_data = ecx.current_expansion.id.expn_data(); Rustc { - resolver: cx.resolver, - sess: cx.parse_sess(), - def_site: cx.with_def_site_ctxt(expn_data.def_site), - call_site: cx.with_call_site_ctxt(expn_data.call_site), - mixed_site: cx.with_mixed_site_ctxt(expn_data.call_site), - span_debug: cx.ecfg.span_debug, + def_site: ecx.with_def_site_ctxt(expn_data.def_site), + call_site: ecx.with_call_site_ctxt(expn_data.call_site), + mixed_site: ecx.with_mixed_site_ctxt(expn_data.call_site), krate: expn_data.macro_def_id.unwrap().krate, rebased_spans: FxHashMap::default(), + ecx, } } + fn sess(&self) -> &ParseSess { + self.ecx.parse_sess() + } + fn lit(&mut self, kind: token::LitKind, symbol: Symbol, suffix: Option<Symbol>) -> Literal { Literal { lit: token::Lit::new(kind, symbol, suffix), span: server::Span::call_site(self) } } } -impl server::Types for Rustc<'_> { +impl server::Types for Rustc<'_, '_> { type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; type TokenStreamBuilder = tokenstream::TokenStreamBuilder; @@ -401,17 +401,20 @@ impl server::Types for Rustc<'_> { type Span = Span; } -impl server::FreeFunctions for Rustc<'_> { +impl server::FreeFunctions for Rustc<'_, '_> { fn track_env_var(&mut self, var: &str, value: Option<&str>) { - self.sess.env_depinfo.borrow_mut().insert((Symbol::intern(var), value.map(Symbol::intern))); + self.sess() + .env_depinfo + .borrow_mut() + .insert((Symbol::intern(var), value.map(Symbol::intern))); } fn track_path(&mut self, path: &str) { - self.sess.file_depinfo.borrow_mut().insert(Symbol::intern(path)); + self.sess().file_depinfo.borrow_mut().insert(Symbol::intern(path)); } } -impl server::TokenStream for Rustc<'_> { +impl server::TokenStream for Rustc<'_, '_> { fn new(&mut self) -> Self::TokenStream { TokenStream::default() } @@ -422,13 +425,61 @@ impl server::TokenStream for Rustc<'_> { parse_stream_from_source_str( FileName::proc_macro_source_code(src), src.to_string(), - self.sess, + self.sess(), Some(self.call_site), ) } fn to_string(&mut self, stream: &Self::TokenStream) -> String { pprust::tts_to_string(stream) } + fn expand_expr(&mut self, stream: &Self::TokenStream) -> Result<Self::TokenStream, ()> { + // Parse the expression from our tokenstream. + let expr: PResult<'_, _> = try { + let mut p = rustc_parse::stream_to_parser( + self.sess(), + stream.clone(), + Some("proc_macro expand expr"), + ); + let expr = p.parse_expr()?; + if p.token != token::Eof { + p.unexpected()?; + } + expr + }; + let expr = expr.map_err(|mut err| err.emit())?; + + // Perform eager expansion on the expression. + let expr = self + .ecx + .expander() + .fully_expand_fragment(crate::expand::AstFragment::Expr(expr)) + .make_expr(); + + // NOTE: For now, limit `expand_expr` to exclusively expand to literals. + // This may be relaxed in the future. + // We don't use `nt_to_tokenstream` as the tokenstream currently cannot + // be recovered in the general case. + match &expr.kind { + ast::ExprKind::Lit(l) => { + Ok(tokenstream::TokenTree::token(token::Literal(l.token), l.span).into()) + } + ast::ExprKind::Unary(ast::UnOp::Neg, e) => match &e.kind { + ast::ExprKind::Lit(l) => match l.token { + token::Lit { kind: token::Integer | token::Float, .. } => { + Ok(Self::TokenStream::from_iter([ + // FIXME: The span of the `-` token is lost when + // parsing, so we cannot faithfully recover it here. + tokenstream::TokenTree::token(token::BinOp(token::Minus), e.span), + tokenstream::TokenTree::token(token::Literal(l.token), l.span), + ])) + } + _ => Err(()), + }, + _ => Err(()), + }, + _ => Err(()), + } + } fn from_token_tree( &mut self, tree: TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>, @@ -440,7 +491,7 @@ impl server::TokenStream for Rustc<'_> { } } -impl server::TokenStreamBuilder for Rustc<'_> { +impl server::TokenStreamBuilder for Rustc<'_, '_> { fn new(&mut self) -> Self::TokenStreamBuilder { tokenstream::TokenStreamBuilder::new() } @@ -452,7 +503,7 @@ impl server::TokenStreamBuilder for Rustc<'_> { } } -impl server::TokenStreamIter for Rustc<'_> { +impl server::TokenStreamIter for Rustc<'_, '_> { fn next( &mut self, iter: &mut Self::TokenStreamIter, @@ -477,7 +528,7 @@ impl server::TokenStreamIter for Rustc<'_> { } } -impl server::Group for Rustc<'_> { +impl server::Group for Rustc<'_, '_> { fn new(&mut self, delimiter: Delimiter, stream: Self::TokenStream) -> Self::Group { Group { delimiter, @@ -506,7 +557,7 @@ impl server::Group for Rustc<'_> { } } -impl server::Punct for Rustc<'_> { +impl server::Punct for Rustc<'_, '_> { fn new(&mut self, ch: char, spacing: Spacing) -> Self::Punct { Punct::new(ch, spacing == Spacing::Joint, server::Span::call_site(self)) } @@ -524,9 +575,9 @@ impl server::Punct for Rustc<'_> { } } -impl server::Ident for Rustc<'_> { +impl server::Ident for Rustc<'_, '_> { fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident { - Ident::new(self.sess, Symbol::intern(string), is_raw, span) + Ident::new(self.sess(), Symbol::intern(string), is_raw, span) } fn span(&mut self, ident: Self::Ident) -> Self::Span { ident.span @@ -536,10 +587,10 @@ impl server::Ident for Rustc<'_> { } } -impl server::Literal for Rustc<'_> { +impl server::Literal for Rustc<'_, '_> { fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> { let name = FileName::proc_macro_source_code(s); - let mut parser = rustc_parse::new_parser_from_source_str(self.sess, name, s.to_owned()); + let mut parser = rustc_parse::new_parser_from_source_str(self.sess(), name, s.to_owned()); let first_span = parser.token.span.data(); let minus_present = parser.eat(&token::BinOp(token::Minus)); @@ -675,7 +726,7 @@ impl server::Literal for Rustc<'_> { } } -impl server::SourceFile for Rustc<'_> { +impl server::SourceFile for Rustc<'_, '_> { fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { Lrc::ptr_eq(file1, file2) } @@ -695,7 +746,7 @@ impl server::SourceFile for Rustc<'_> { } } -impl server::MultiSpan for Rustc<'_> { +impl server::MultiSpan for Rustc<'_, '_> { fn new(&mut self) -> Self::MultiSpan { vec![] } @@ -704,7 +755,7 @@ impl server::MultiSpan for Rustc<'_> { } } -impl server::Diagnostic for Rustc<'_> { +impl server::Diagnostic for Rustc<'_, '_> { fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic { let mut diag = Diagnostic::new(level.to_internal(), msg); diag.set_span(MultiSpan::from_spans(spans)); @@ -720,13 +771,13 @@ impl server::Diagnostic for Rustc<'_> { diag.sub(level.to_internal(), msg, MultiSpan::from_spans(spans), None); } fn emit(&mut self, diag: Self::Diagnostic) { - self.sess.span_diagnostic.emit_diagnostic(&diag); + self.sess().span_diagnostic.emit_diagnostic(&diag); } } -impl server::Span for Rustc<'_> { +impl server::Span for Rustc<'_, '_> { fn debug(&mut self, span: Self::Span) -> String { - if self.span_debug { + if self.ecx.ecfg.span_debug { format!("{:?}", span) } else { format!("{:?} bytes({}..{})", span.ctxt(), span.lo().0, span.hi().0) @@ -742,7 +793,7 @@ impl server::Span for Rustc<'_> { self.mixed_site } fn source_file(&mut self, span: Self::Span) -> Self::SourceFile { - self.sess.source_map().lookup_char_pos(span.lo()).file + self.sess().source_map().lookup_char_pos(span.lo()).file } fn parent(&mut self, span: Self::Span) -> Option<Self::Span> { span.parent_callsite() @@ -751,11 +802,11 @@ impl server::Span for Rustc<'_> { span.source_callsite() } fn start(&mut self, span: Self::Span) -> LineColumn { - let loc = self.sess.source_map().lookup_char_pos(span.lo()); + let loc = self.sess().source_map().lookup_char_pos(span.lo()); LineColumn { line: loc.line, column: loc.col.to_usize() } } fn end(&mut self, span: Self::Span) -> LineColumn { - let loc = self.sess.source_map().lookup_char_pos(span.hi()); + let loc = self.sess().source_map().lookup_char_pos(span.hi()); LineColumn { line: loc.line, column: loc.col.to_usize() } } fn before(&mut self, span: Self::Span) -> Self::Span { @@ -765,8 +816,8 @@ impl server::Span for Rustc<'_> { span.shrink_to_hi() } fn join(&mut self, first: Self::Span, second: Self::Span) -> Option<Self::Span> { - let self_loc = self.sess.source_map().lookup_char_pos(first.lo()); - let other_loc = self.sess.source_map().lookup_char_pos(second.lo()); + let self_loc = self.sess().source_map().lookup_char_pos(first.lo()); + let other_loc = self.sess().source_map().lookup_char_pos(second.lo()); if self_loc.file.name != other_loc.file.name { return None; @@ -778,7 +829,7 @@ impl server::Span for Rustc<'_> { span.with_ctxt(at.ctxt()) } fn source_text(&mut self, span: Self::Span) -> Option<String> { - self.sess.source_map().span_to_snippet(span).ok() + self.sess().source_map().span_to_snippet(span).ok() } /// Saves the provided span into the metadata of /// *the crate we are currently compiling*, which must @@ -805,10 +856,10 @@ impl server::Span for Rustc<'_> { /// since we've loaded `my_proc_macro` from disk in order to execute it). /// In this way, we have obtained a span pointing into `my_proc_macro` fn save_span(&mut self, span: Self::Span) -> usize { - self.sess.save_proc_macro_span(span) + self.sess().save_proc_macro_span(span) } fn recover_proc_macro_span(&mut self, id: usize) -> Self::Span { - let (resolver, krate, def_site) = (self.resolver, self.krate, self.def_site); + let (resolver, krate, def_site) = (&*self.ecx.resolver, self.krate, self.def_site); *self.rebased_spans.entry(id).or_insert_with(|| { // FIXME: `SyntaxContext` for spans from proc macro crates is lost during encoding, // replace it with a def-site context until we are encoding it properly. @@ -821,11 +872,11 @@ impl server::Span for Rustc<'_> { fn ident_name_compatibility_hack( nt: &Nonterminal, orig_span: Span, - rustc: &mut Rustc<'_>, + rustc: &mut Rustc<'_, '_>, ) -> Option<(rustc_span::symbol::Ident, bool)> { if let NtIdent(ident, is_raw) = nt { if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind { - let source_map = rustc.sess.source_map(); + let source_map = rustc.sess().source_map(); let filename = source_map.span_to_filename(orig_span); if let FileName::Real(RealFileName::LocalPath(path)) = filename { let matches_prefix = |prefix, filename| { @@ -846,7 +897,7 @@ fn ident_name_compatibility_hack( let snippet = source_map.span_to_snippet(orig_span); if snippet.as_deref() == Ok("$name") { if time_macros_impl { - rustc.sess.buffer_lint_with_diagnostic( + rustc.sess().buffer_lint_with_diagnostic( &PROC_MACRO_BACK_COMPAT, orig_span, ast::CRATE_NODE_ID, @@ -871,7 +922,7 @@ fn ident_name_compatibility_hack( .and_then(|c| c.parse::<u32>().ok()) .map_or(false, |v| v < 40) { - rustc.sess.buffer_lint_with_diagnostic( + rustc.sess().buffer_lint_with_diagnostic( &PROC_MACRO_BACK_COMPAT, orig_span, ast::CRATE_NODE_ID, @@ -894,7 +945,7 @@ fn ident_name_compatibility_hack( source_map.span_to_filename(rustc.def_site) { if macro_path.to_string_lossy().contains("pin-project-internal-0.") { - rustc.sess.buffer_lint_with_diagnostic( + rustc.sess().buffer_lint_with_diagnostic( &PROC_MACRO_BACK_COMPAT, orig_span, ast::CRATE_NODE_ID, |
