rust/compiler/rustc_expand/src/expand.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

2354 lines
92 KiB
Rust
Raw Normal View History

use std::ops::Deref;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
use std::{iter, mem};
use rustc_ast as ast;
use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter};
use rustc_ast::tokenstream::TokenStream;
2024-02-24 15:22:42 -05:00
use rustc_ast::visit::{self, AssocCtxt, Visitor, VisitorResult, try_visit, walk_list};
use rustc_ast::{
AssocItemKind, AstNodeWrapper, AttrArgs, AttrStyle, AttrVec, ExprKind, ForeignItemKind,
2024-10-04 21:59:04 +09:00
HasAttrs, HasNodeId, Inline, ItemKind, MacStmtStyle, MetaItemInner, MetaItemKind, ModKind,
NodeId, PatKind, StmtKind, TyKind,
};
use rustc_ast_pretty::pprust;
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
use rustc_errors::PResult;
use rustc_feature::Features;
use rustc_parse::parser::{
AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma,
token_descr,
};
use rustc_parse::validate_attr;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
use rustc_session::parse::feature_err;
use rustc_session::{Limit, Session};
use rustc_span::hygiene::SyntaxContext;
use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, sym};
use smallvec::SmallVec;
use crate::base::*;
use crate::config::{StripUnconfigured, attr_into_trace};
use crate::errors::{
EmptyDelegationMac, GlobDelegationOutsideImpls, GlobDelegationTraitlessQpath, IncompleteParse,
RecursionLimitReached, RemoveExprNotSupported, RemoveNodeNotSupported, UnsupportedKeyValue,
WrongFragmentKind,
};
use crate::fluent_generated;
use crate::mbe::diagnostics::annotate_err_with_kind;
use crate::module::{
DirOwnership, ParsedExternalMod, mod_dir_path, mod_file_path_from_attr, parse_external_mod,
};
2021-05-18 21:46:41 -04:00
use crate::placeholders::{PlaceholderExpander, placeholder};
macro_rules! ast_fragments {
(
$($Kind:ident($AstTy:ty) {
$kind_name:expr;
$(one fn $mut_visit_ast:ident; fn $visit_ast:ident;)?
$(many fn $flat_map_ast_elt:ident; fn $visit_ast_elt:ident($($args:tt)*);)?
fn $make_ast:ident;
})*
) => {
/// A fragment of AST that can be produced by a single macro expansion.
/// Can also serve as an input and intermediate result for macro expansion operations.
pub enum AstFragment {
OptExpr(Option<P<ast::Expr>>),
MethodReceiverExpr(P<ast::Expr>),
$($Kind($AstTy),)*
}
/// "Discriminant" of an AST fragment.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum AstFragmentKind {
OptExpr,
MethodReceiverExpr,
$($Kind,)*
}
impl AstFragmentKind {
pub fn name(self) -> &'static str {
match self {
AstFragmentKind::OptExpr => "expression",
AstFragmentKind::MethodReceiverExpr => "expression",
$(AstFragmentKind::$Kind => $kind_name,)*
}
}
fn make_from<'a>(self, result: Box<dyn MacResult + 'a>) -> Option<AstFragment> {
match self {
AstFragmentKind::OptExpr =>
result.make_expr().map(Some).map(AstFragment::OptExpr),
AstFragmentKind::MethodReceiverExpr =>
result.make_expr().map(AstFragment::MethodReceiverExpr),
$(AstFragmentKind::$Kind => result.$make_ast().map(AstFragment::$Kind),)*
}
}
}
impl AstFragment {
2024-05-01 09:39:07 +10:00
fn add_placeholders(&mut self, placeholders: &[NodeId]) {
if placeholders.is_empty() {
return;
}
match self {
$($(AstFragment::$Kind(ast) => ast.extend(placeholders.iter().flat_map(|id| {
${ignore($flat_map_ast_elt)}
placeholder(AstFragmentKind::$Kind, *id, None).$make_ast()
})),)?)*
_ => panic!("unexpected AST fragment kind")
}
}
2024-05-01 09:39:07 +10:00
pub(crate) fn make_opt_expr(self) -> Option<P<ast::Expr>> {
match self {
AstFragment::OptExpr(expr) => expr,
_ => panic!("AstFragment::make_* called on the wrong kind of fragment"),
}
}
2024-05-01 09:39:07 +10:00
pub(crate) fn make_method_receiver_expr(self) -> P<ast::Expr> {
match self {
AstFragment::MethodReceiverExpr(expr) => expr,
_ => panic!("AstFragment::make_* called on the wrong kind of fragment"),
}
}
$(pub fn $make_ast(self) -> $AstTy {
match self {
AstFragment::$Kind(ast) => ast,
_ => panic!("AstFragment::make_* called on the wrong kind of fragment"),
}
})*
2016-05-19 09:45:37 +00:00
fn make_ast<T: InvocationCollectorNode>(self) -> T::OutputTy {
T::fragment_to_output(self)
}
2024-05-01 09:39:07 +10:00
pub(crate) fn mut_visit_with<F: MutVisitor>(&mut self, vis: &mut F) {
match self {
Overhaul `syntax::fold::Folder`. This commit changes `syntax::fold::Folder` from a functional style (where most methods take a `T` and produce a new `T`) to a more imperative style (where most methods take and modify a `&mut T`), and renames it `syntax::mut_visit::MutVisitor`. The first benefit is speed. The functional style does not require any reallocations, due to the use of `P::map` and `MoveMap::move_{,flat_}map`. However, every field in the AST must be overwritten; even those fields that are unchanged are overwritten with the same value. This causes a lot of unnecessary memory writes. The imperative style reduces instruction counts by 1--3% across a wide range of workloads, particularly incremental workloads. The second benefit is conciseness; the imperative style is usually more concise. E.g. compare the old functional style: ``` fn fold_abc(&mut self, abc: ABC) { ABC { a: fold_a(abc.a), b: fold_b(abc.b), c: abc.c, } } ``` with the imperative style: ``` fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) { visit_a(a); visit_b(b); } ``` (The reductions get larger in more complex examples.) Overall, the patch removes over 200 lines of code -- even though the new code has more comments -- and a lot of the remaining lines have fewer characters. Some notes: - The old style used methods called `fold_*`. The new style mostly uses methods called `visit_*`, but there are a few methods that map a `T` to something other than a `T`, which are called `flat_map_*` (`T` maps to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s). - `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed `map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to reflect their slightly changed signatures. - Although this commit renames the `fold` module as `mut_visit`, it keeps it in the `fold.rs` file, so as not to confuse git. The next commit will rename the file.
2019-02-05 15:20:55 +11:00
AstFragment::OptExpr(opt_expr) => {
visit_clobber(opt_expr, |opt_expr| {
if let Some(expr) = opt_expr {
vis.filter_map_expr(expr)
} else {
None
}
});
}
AstFragment::MethodReceiverExpr(expr) => vis.visit_method_receiver_expr(expr),
$($(AstFragment::$Kind(ast) => vis.$mut_visit_ast(ast),)?)*
$($(AstFragment::$Kind(ast) =>
ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast, $($args)*)),)?)*
}
2016-05-19 09:45:37 +00:00
}
pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) -> V::Result {
2022-12-23 17:34:23 +00:00
match self {
AstFragment::OptExpr(Some(expr)) => try_visit!(visitor.visit_expr(expr)),
AstFragment::OptExpr(None) => {}
AstFragment::MethodReceiverExpr(expr) => try_visit!(visitor.visit_method_receiver_expr(expr)),
$($(AstFragment::$Kind(ast) => try_visit!(visitor.$visit_ast(ast)),)?)*
$($(AstFragment::$Kind(ast) => walk_list!(visitor, $visit_ast_elt, &ast[..], $($args)*),)?)*
}
V::Result::output()
}
2016-05-19 09:45:37 +00:00
}
2016-09-02 09:12:47 +00:00
impl<'a> MacResult for crate::mbe::macro_rules::ParserAnyMacro<'a> {
$(fn $make_ast(self: Box<crate::mbe::macro_rules::ParserAnyMacro<'a>>)
-> Option<$AstTy> {
Some(self.make(AstFragmentKind::$Kind).$make_ast())
})*
}
}
2016-05-19 09:45:37 +00:00
}
ast_fragments! {
Overhaul `syntax::fold::Folder`. This commit changes `syntax::fold::Folder` from a functional style (where most methods take a `T` and produce a new `T`) to a more imperative style (where most methods take and modify a `&mut T`), and renames it `syntax::mut_visit::MutVisitor`. The first benefit is speed. The functional style does not require any reallocations, due to the use of `P::map` and `MoveMap::move_{,flat_}map`. However, every field in the AST must be overwritten; even those fields that are unchanged are overwritten with the same value. This causes a lot of unnecessary memory writes. The imperative style reduces instruction counts by 1--3% across a wide range of workloads, particularly incremental workloads. The second benefit is conciseness; the imperative style is usually more concise. E.g. compare the old functional style: ``` fn fold_abc(&mut self, abc: ABC) { ABC { a: fold_a(abc.a), b: fold_b(abc.b), c: abc.c, } } ``` with the imperative style: ``` fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) { visit_a(a); visit_b(b); } ``` (The reductions get larger in more complex examples.) Overall, the patch removes over 200 lines of code -- even though the new code has more comments -- and a lot of the remaining lines have fewer characters. Some notes: - The old style used methods called `fold_*`. The new style mostly uses methods called `visit_*`, but there are a few methods that map a `T` to something other than a `T`, which are called `flat_map_*` (`T` maps to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s). - `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed `map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to reflect their slightly changed signatures. - Although this commit renames the `fold` module as `mut_visit`, it keeps it in the `fold.rs` file, so as not to confuse git. The next commit will rename the file.
2019-02-05 15:20:55 +11:00
Expr(P<ast::Expr>) { "expression"; one fn visit_expr; fn visit_expr; fn make_expr; }
Pat(P<ast::Pat>) { "pattern"; one fn visit_pat; fn visit_pat; fn make_pat; }
Ty(P<ast::Ty>) { "type"; one fn visit_ty; fn visit_ty; fn make_ty; }
2018-08-30 11:42:16 +02:00
Stmts(SmallVec<[ast::Stmt; 1]>) {
"statement"; many fn flat_map_stmt; fn visit_stmt(); fn make_stmts;
2018-08-30 11:42:16 +02:00
}
Items(SmallVec<[P<ast::Item>; 1]>) {
"item"; many fn flat_map_item; fn visit_item(); fn make_items;
2018-08-30 11:42:16 +02:00
}
TraitItems(SmallVec<[P<ast::AssocItem>; 1]>) {
"trait item";
many fn flat_map_assoc_item;
fn visit_assoc_item(AssocCtxt::Trait);
fn make_trait_items;
}
ImplItems(SmallVec<[P<ast::AssocItem>; 1]>) {
"impl item";
many fn flat_map_assoc_item;
fn visit_assoc_item(AssocCtxt::Impl { of_trait: false });
fn make_impl_items;
}
TraitImplItems(SmallVec<[P<ast::AssocItem>; 1]>) {
"impl item";
many fn flat_map_assoc_item;
fn visit_assoc_item(AssocCtxt::Impl { of_trait: true });
fn make_trait_impl_items;
}
ForeignItems(SmallVec<[P<ast::ForeignItem>; 1]>) {
"foreign item";
many fn flat_map_foreign_item;
fn visit_foreign_item();
fn make_foreign_items;
}
Arms(SmallVec<[ast::Arm; 1]>) {
"match arm"; many fn flat_map_arm; fn visit_arm(); fn make_arms;
}
ExprFields(SmallVec<[ast::ExprField; 1]>) {
"field expression"; many fn flat_map_expr_field; fn visit_expr_field(); fn make_expr_fields;
}
PatFields(SmallVec<[ast::PatField; 1]>) {
"field pattern";
many fn flat_map_pat_field;
fn visit_pat_field();
fn make_pat_fields;
}
GenericParams(SmallVec<[ast::GenericParam; 1]>) {
"generic parameter";
many fn flat_map_generic_param;
fn visit_generic_param();
fn make_generic_params;
}
Params(SmallVec<[ast::Param; 1]>) {
"function parameter"; many fn flat_map_param; fn visit_param(); fn make_params;
}
FieldDefs(SmallVec<[ast::FieldDef; 1]>) {
"field";
many fn flat_map_field_def;
fn visit_field_def();
fn make_field_defs;
}
Variants(SmallVec<[ast::Variant; 1]>) {
"variant"; many fn flat_map_variant; fn visit_variant(); fn make_variants;
}
WherePredicates(SmallVec<[ast::WherePredicate; 1]>) {
"where predicate";
many fn flat_map_where_predicate;
fn visit_where_predicate();
fn make_where_predicates;
}
Crate(ast::Crate) { "crate"; one fn visit_crate; fn visit_crate; fn make_crate; }
2016-05-19 09:45:37 +00:00
}
pub enum SupportsMacroExpansion {
No,
Yes { supports_inner_attrs: bool },
}
impl AstFragmentKind {
pub(crate) fn dummy(self, span: Span, guar: ErrorGuaranteed) -> AstFragment {
self.make_from(DummyResult::any(span, guar)).expect("couldn't create a dummy AST fragment")
}
pub fn supports_macro_expansion(self) -> SupportsMacroExpansion {
match self {
AstFragmentKind::OptExpr
| AstFragmentKind::Expr
| AstFragmentKind::MethodReceiverExpr
| AstFragmentKind::Stmts
| AstFragmentKind::Ty
| AstFragmentKind::Pat => SupportsMacroExpansion::Yes { supports_inner_attrs: false },
AstFragmentKind::Items
| AstFragmentKind::TraitItems
| AstFragmentKind::ImplItems
| AstFragmentKind::TraitImplItems
| AstFragmentKind::ForeignItems
| AstFragmentKind::Crate => SupportsMacroExpansion::Yes { supports_inner_attrs: true },
AstFragmentKind::Arms
| AstFragmentKind::ExprFields
| AstFragmentKind::PatFields
| AstFragmentKind::GenericParams
| AstFragmentKind::Params
| AstFragmentKind::FieldDefs
| AstFragmentKind::Variants
| AstFragmentKind::WherePredicates => SupportsMacroExpansion::No,
}
}
fn expect_from_annotatables<I: IntoIterator<Item = Annotatable>>(
self,
items: I,
) -> AstFragment {
let mut items = items.into_iter();
match self {
AstFragmentKind::Arms => {
AstFragment::Arms(items.map(Annotatable::expect_arm).collect())
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::ExprFields => {
AstFragment::ExprFields(items.map(Annotatable::expect_expr_field).collect())
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::PatFields => {
AstFragment::PatFields(items.map(Annotatable::expect_pat_field).collect())
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::GenericParams => {
AstFragment::GenericParams(items.map(Annotatable::expect_generic_param).collect())
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::Params => {
AstFragment::Params(items.map(Annotatable::expect_param).collect())
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::FieldDefs => {
AstFragment::FieldDefs(items.map(Annotatable::expect_field_def).collect())
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::Variants => {
AstFragment::Variants(items.map(Annotatable::expect_variant).collect())
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::WherePredicates => AstFragment::WherePredicates(
items.map(Annotatable::expect_where_predicate).collect(),
),
AstFragmentKind::Items => {
AstFragment::Items(items.map(Annotatable::expect_item).collect())
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::ImplItems => {
AstFragment::ImplItems(items.map(Annotatable::expect_impl_item).collect())
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::TraitImplItems => {
AstFragment::TraitImplItems(items.map(Annotatable::expect_impl_item).collect())
}
AstFragmentKind::TraitItems => {
AstFragment::TraitItems(items.map(Annotatable::expect_trait_item).collect())
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::ForeignItems => {
AstFragment::ForeignItems(items.map(Annotatable::expect_foreign_item).collect())
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::Stmts => {
AstFragment::Stmts(items.map(Annotatable::expect_stmt).collect())
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::Expr => AstFragment::Expr(
items.next().expect("expected exactly one expression").expect_expr(),
),
AstFragmentKind::MethodReceiverExpr => AstFragment::MethodReceiverExpr(
items.next().expect("expected exactly one expression").expect_expr(),
),
AstFragmentKind::OptExpr => {
AstFragment::OptExpr(items.next().map(Annotatable::expect_expr))
2019-12-22 17:42:04 -05:00
}
AstFragmentKind::Crate => {
AstFragment::Crate(items.next().expect("expected exactly one crate").expect_crate())
}
AstFragmentKind::Pat | AstFragmentKind::Ty => {
panic!("patterns and types aren't annotatable")
2019-12-22 17:42:04 -05:00
}
}
}
}
pub struct Invocation {
pub kind: InvocationKind,
pub fragment_kind: AstFragmentKind,
pub expansion_data: ExpansionData,
}
pub enum InvocationKind {
Bang {
2022-08-12 12:20:10 +10:00
mac: P<ast::MacCall>,
span: Span,
},
Attr {
attr: ast::Attribute,
/// Re-insertion position for inert attributes.
pos: usize,
item: Annotatable,
/// Required for resolving derive helper attributes.
2022-01-07 14:54:16 +08:00
derives: Vec<ast::Path>,
},
Derive {
2022-01-07 14:54:16 +08:00
path: ast::Path,
2022-09-20 11:55:07 +00:00
is_const: bool,
item: Annotatable,
},
2024-03-15 14:21:03 +03:00
GlobDelegation {
item: P<ast::AssocItem>,
/// Whether this is a trait impl or an inherent impl
of_trait: bool,
2024-03-15 14:21:03 +03:00
},
}
impl InvocationKind {
fn placeholder_visibility(&self) -> Option<ast::Visibility> {
// HACK: For unnamed fields placeholders should have the same visibility as the actual
// fields because for tuple structs/variants resolve determines visibilities of their
2022-10-14 00:25:34 +08:00
// constructor using these field visibilities before attributes on them are expanded.
// The assumption is that the attribute expansion cannot change field visibilities,
// and it holds because only inert attributes are supported in this position.
match self {
InvocationKind::Attr { item: Annotatable::FieldDef(field), .. }
| InvocationKind::Derive { item: Annotatable::FieldDef(field), .. }
if field.ident.is_none() =>
2019-12-22 17:42:04 -05:00
{
Some(field.vis.clone())
2019-12-22 17:42:04 -05:00
}
_ => None,
}
}
}
impl Invocation {
pub fn span(&self) -> Span {
match &self.kind {
InvocationKind::Bang { span, .. } => *span,
InvocationKind::Attr { attr, .. } => attr.span,
InvocationKind::Derive { path, .. } => path.span,
InvocationKind::GlobDelegation { item, .. } => item.span,
}
}
fn span_mut(&mut self) -> &mut Span {
match &mut self.kind {
InvocationKind::Bang { span, .. } => span,
InvocationKind::Attr { attr, .. } => &mut attr.span,
InvocationKind::Derive { path, .. } => &mut path.span,
InvocationKind::GlobDelegation { item, .. } => &mut item.span,
}
}
}
pub struct MacroExpander<'a, 'b> {
pub cx: &'a mut ExtCtxt<'b>,
monotonic: bool, // cf. `cx.monotonic_expander()`
2014-12-14 15:42:41 +13:00
}
impl<'a, 'b> MacroExpander<'a, 'b> {
pub fn new(cx: &'a mut ExtCtxt<'b>, monotonic: bool) -> Self {
2019-06-25 23:22:45 +02:00
MacroExpander { cx, monotonic }
2014-12-14 15:42:41 +13:00
}
pub fn expand_crate(&mut self, krate: ast::Crate) -> ast::Crate {
2022-03-03 18:45:25 -05:00
let file_path = match self.cx.source_map().span_to_filename(krate.spans.inner_span) {
FileName::Real(name) => name
.into_local_path()
.expect("attempting to resolve a file path in an external file"),
other => PathBuf::from(other.prefer_local().to_string()),
};
let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
self.cx.root_path = dir_path.clone();
self.cx.current_expansion.module = Rc::new(ModuleData {
mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)],
file_path_stack: vec![file_path],
dir_path,
});
let krate = self.fully_expand_fragment(AstFragment::Crate(krate)).make_crate();
assert_eq!(krate.id, ast::CRATE_NODE_ID);
self.cx.trace_macros_diag();
2016-09-02 09:12:47 +00:00
krate
}
/// Recursively expand all macro invocations in this AST fragment.
pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragment {
let orig_expansion_data = self.cx.current_expansion.clone();
let orig_force_mode = self.cx.force_mode;
2018-06-23 19:27:01 +03:00
// Collect all macro invocations and replace them with placeholders.
Overhaul `syntax::fold::Folder`. This commit changes `syntax::fold::Folder` from a functional style (where most methods take a `T` and produce a new `T`) to a more imperative style (where most methods take and modify a `&mut T`), and renames it `syntax::mut_visit::MutVisitor`. The first benefit is speed. The functional style does not require any reallocations, due to the use of `P::map` and `MoveMap::move_{,flat_}map`. However, every field in the AST must be overwritten; even those fields that are unchanged are overwritten with the same value. This causes a lot of unnecessary memory writes. The imperative style reduces instruction counts by 1--3% across a wide range of workloads, particularly incremental workloads. The second benefit is conciseness; the imperative style is usually more concise. E.g. compare the old functional style: ``` fn fold_abc(&mut self, abc: ABC) { ABC { a: fold_a(abc.a), b: fold_b(abc.b), c: abc.c, } } ``` with the imperative style: ``` fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) { visit_a(a); visit_b(b); } ``` (The reductions get larger in more complex examples.) Overall, the patch removes over 200 lines of code -- even though the new code has more comments -- and a lot of the remaining lines have fewer characters. Some notes: - The old style used methods called `fold_*`. The new style mostly uses methods called `visit_*`, but there are a few methods that map a `T` to something other than a `T`, which are called `flat_map_*` (`T` maps to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s). - `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed `map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to reflect their slightly changed signatures. - Although this commit renames the `fold` module as `mut_visit`, it keeps it in the `fold.rs` file, so as not to confuse git. The next commit will rename the file.
2019-02-05 15:20:55 +11:00
let (mut fragment_with_placeholders, mut invocations) =
self.collect_invocations(input_fragment, &[]);
2018-06-23 19:27:01 +03:00
// Optimization: if we resolve all imports now,
// we'll be able to immediately resolve most of imported macros.
2016-11-10 10:11:25 +00:00
self.resolve_imports();
2016-09-02 09:12:47 +00:00
2018-08-19 15:30:23 +02:00
// Resolve paths in all invocations and produce output expanded fragments for them, but
2018-06-23 19:27:01 +03:00
// do not insert them into our input AST fragment yet, only store in `expanded_fragments`.
// The output fragments also go through expansion recursively until no invocations are left.
// Unresolved macros produce dummy outputs as a recovery measure.
invocations.reverse();
let mut expanded_fragments = Vec::new();
let mut undetermined_invocations = Vec::new();
let (mut progress, mut force) = (false, !self.monotonic);
loop {
let Some((invoc, ext)) = invocations.pop() else {
2016-11-10 10:11:25 +00:00
self.resolve_imports();
if undetermined_invocations.is_empty() {
break;
}
2019-06-30 11:30:01 -07:00
invocations = mem::take(&mut undetermined_invocations);
force = !progress;
progress = false;
if force && self.monotonic {
self.cx.dcx().span_delayed_bug(
invocations.last().unwrap().0.span(),
"expansion entered force mode without producing any errors",
);
}
continue;
};
let ext = match ext {
Some(ext) => ext,
None => {
let eager_expansion_root = if self.monotonic {
invoc.expansion_data.id
} else {
orig_expansion_data.id
};
match self.cx.resolver.resolve_macro_invocation(
&invoc,
eager_expansion_root,
force,
) {
Ok(ext) => ext,
Err(Indeterminate) => {
// Cannot resolve, will retry this invocation later.
undetermined_invocations.push((invoc, None));
continue;
}
}
}
};
let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data;
let depth = depth - orig_expansion_data.depth;
self.cx.current_expansion = invoc.expansion_data.clone();
self.cx.force_mode = force;
let fragment_kind = invoc.fragment_kind;
match self.expand_invoc(invoc, &ext.kind) {
ExpandResult::Ready(fragment) => {
let mut derive_invocations = Vec::new();
let derive_placeholders = self
.cx
.resolver
.take_derive_resolutions(expn_id)
.map(|derives| {
derive_invocations.reserve(derives.len());
derives
.into_iter()
.map(|DeriveResolution { path, item, exts: _, is_const }| {
// FIXME: Consider using the derive resolutions (`_exts`)
// instead of enqueuing the derives to be resolved again later.
2021-06-25 20:43:04 +02:00
let expn_id = LocalExpnId::fresh_empty();
derive_invocations.push((
Invocation {
2022-09-20 11:55:07 +00:00
kind: InvocationKind::Derive { path, item, is_const },
fragment_kind,
expansion_data: ExpansionData {
id: expn_id,
..self.cx.current_expansion.clone()
},
},
None,
));
NodeId::placeholder_from_expn_id(expn_id)
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
let (expanded_fragment, collected_invocations) =
self.collect_invocations(fragment, &derive_placeholders);
2024-05-01 09:27:20 +10:00
// We choose to expand any derive invocations associated with this macro
// invocation *before* any macro invocations collected from the output
// fragment.
derive_invocations.extend(collected_invocations);
progress = true;
if expanded_fragments.len() < depth {
expanded_fragments.push(Vec::new());
}
expanded_fragments[depth - 1].push((expn_id, expanded_fragment));
invocations.extend(derive_invocations.into_iter().rev());
}
ExpandResult::Retry(invoc) => {
if force {
self.cx.dcx().span_bug(
invoc.span(),
"expansion entered force mode but is still stuck",
);
} else {
// Cannot expand, will retry this invocation later.
undetermined_invocations.push((invoc, Some(ext)));
}
}
}
2016-09-02 09:12:47 +00:00
}
self.cx.current_expansion = orig_expansion_data;
self.cx.force_mode = orig_force_mode;
2018-06-23 19:27:01 +03:00
// Finally incorporate all the expanded macros into the input AST fragment.
let mut placeholder_expander = PlaceholderExpander::default();
while let Some(expanded_fragments) = expanded_fragments.pop() {
for (expn_id, expanded_fragment) in expanded_fragments.into_iter().rev() {
placeholder_expander
.add(NodeId::placeholder_from_expn_id(expn_id), expanded_fragment);
}
}
Overhaul `syntax::fold::Folder`. This commit changes `syntax::fold::Folder` from a functional style (where most methods take a `T` and produce a new `T`) to a more imperative style (where most methods take and modify a `&mut T`), and renames it `syntax::mut_visit::MutVisitor`. The first benefit is speed. The functional style does not require any reallocations, due to the use of `P::map` and `MoveMap::move_{,flat_}map`. However, every field in the AST must be overwritten; even those fields that are unchanged are overwritten with the same value. This causes a lot of unnecessary memory writes. The imperative style reduces instruction counts by 1--3% across a wide range of workloads, particularly incremental workloads. The second benefit is conciseness; the imperative style is usually more concise. E.g. compare the old functional style: ``` fn fold_abc(&mut self, abc: ABC) { ABC { a: fold_a(abc.a), b: fold_b(abc.b), c: abc.c, } } ``` with the imperative style: ``` fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) { visit_a(a); visit_b(b); } ``` (The reductions get larger in more complex examples.) Overall, the patch removes over 200 lines of code -- even though the new code has more comments -- and a lot of the remaining lines have fewer characters. Some notes: - The old style used methods called `fold_*`. The new style mostly uses methods called `visit_*`, but there are a few methods that map a `T` to something other than a `T`, which are called `flat_map_*` (`T` maps to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s). - `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed `map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to reflect their slightly changed signatures. - Although this commit renames the `fold` module as `mut_visit`, it keeps it in the `fold.rs` file, so as not to confuse git. The next commit will rename the file.
2019-02-05 15:20:55 +11:00
fragment_with_placeholders.mut_visit_with(&mut placeholder_expander);
fragment_with_placeholders
2016-09-02 09:12:47 +00:00
}
2016-11-10 10:11:25 +00:00
fn resolve_imports(&mut self) {
if self.monotonic {
self.cx.resolver.resolve_imports();
}
}
2019-02-08 14:53:55 +01:00
/// Collects all macro invocations reachable at this time in this AST fragment, and replace
2018-06-23 19:27:01 +03:00
/// them with "placeholders" - dummy macro invocations with specially crafted `NodeId`s.
/// Then call into resolver that builds a skeleton ("reduced graph") of the fragment and
/// prepares data for resolving paths of macro invocations.
fn collect_invocations(
&mut self,
mut fragment: AstFragment,
extra_placeholders: &[NodeId],
) -> (AstFragment, Vec<(Invocation, Option<Arc<SyntaxExtension>>)>) {
// Resolve `$crate`s in the fragment for pretty-printing.
self.cx.resolver.resolve_dollar_crates();
2021-05-02 21:19:28 +02:00
let mut invocations = {
let mut collector = InvocationCollector {
Implement token-based handling of attributes during expansion This PR modifies the macro expansion infrastructure to handle attributes in a fully token-based manner. As a result: * Derives macros no longer lose spans when their input is modified by eager cfg-expansion. This is accomplished by performing eager cfg-expansion on the token stream that we pass to the derive proc-macro * Inner attributes now preserve spans in all cases, including when we have multiple inner attributes in a row. This is accomplished through the following changes: * New structs `AttrAnnotatedTokenStream` and `AttrAnnotatedTokenTree` are introduced. These are very similar to a normal `TokenTree`, but they also track the position of attributes and attribute targets within the stream. They are built when we collect tokens during parsing. An `AttrAnnotatedTokenStream` is converted to a regular `TokenStream` when we invoke a macro. * Token capturing and `LazyTokenStream` are modified to work with `AttrAnnotatedTokenStream`. A new `ReplaceRange` type is introduced, which is created during the parsing of a nested AST node to make the 'outer' AST node aware of the attributes and attribute target stored deeper in the token stream. * When we need to perform eager cfg-expansion (either due to `#[derive]` or `#[cfg_eval]`), we tokenize and reparse our target, capturing additional information about the locations of `#[cfg]` and `#[cfg_attr]` attributes at any depth within the target. This is a performance optimization, allowing us to perform less work in the typical case where captured tokens never have eager cfg-expansion run.
2020-11-28 18:33:17 -05:00
// Non-derive macro invocations cannot see the results of cfg expansion - they
// will either be removed along with the item, or invoked before the cfg/cfg_attr
// attribute is expanded. Therefore, we don't need to configure the tokens
// Derive macros *can* see the results of cfg-expansion - they are handled
// specially in `fully_expand_fragment`
cx: self.cx,
invocations: Vec::new(),
monotonic: self.monotonic,
};
Overhaul `syntax::fold::Folder`. This commit changes `syntax::fold::Folder` from a functional style (where most methods take a `T` and produce a new `T`) to a more imperative style (where most methods take and modify a `&mut T`), and renames it `syntax::mut_visit::MutVisitor`. The first benefit is speed. The functional style does not require any reallocations, due to the use of `P::map` and `MoveMap::move_{,flat_}map`. However, every field in the AST must be overwritten; even those fields that are unchanged are overwritten with the same value. This causes a lot of unnecessary memory writes. The imperative style reduces instruction counts by 1--3% across a wide range of workloads, particularly incremental workloads. The second benefit is conciseness; the imperative style is usually more concise. E.g. compare the old functional style: ``` fn fold_abc(&mut self, abc: ABC) { ABC { a: fold_a(abc.a), b: fold_b(abc.b), c: abc.c, } } ``` with the imperative style: ``` fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) { visit_a(a); visit_b(b); } ``` (The reductions get larger in more complex examples.) Overall, the patch removes over 200 lines of code -- even though the new code has more comments -- and a lot of the remaining lines have fewer characters. Some notes: - The old style used methods called `fold_*`. The new style mostly uses methods called `visit_*`, but there are a few methods that map a `T` to something other than a `T`, which are called `flat_map_*` (`T` maps to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s). - `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed `map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to reflect their slightly changed signatures. - Although this commit renames the `fold` module as `mut_visit`, it keeps it in the `fold.rs` file, so as not to confuse git. The next commit will rename the file.
2019-02-05 15:20:55 +11:00
fragment.mut_visit_with(&mut collector);
fragment.add_placeholders(extra_placeholders);
Overhaul `syntax::fold::Folder`. This commit changes `syntax::fold::Folder` from a functional style (where most methods take a `T` and produce a new `T`) to a more imperative style (where most methods take and modify a `&mut T`), and renames it `syntax::mut_visit::MutVisitor`. The first benefit is speed. The functional style does not require any reallocations, due to the use of `P::map` and `MoveMap::move_{,flat_}map`. However, every field in the AST must be overwritten; even those fields that are unchanged are overwritten with the same value. This causes a lot of unnecessary memory writes. The imperative style reduces instruction counts by 1--3% across a wide range of workloads, particularly incremental workloads. The second benefit is conciseness; the imperative style is usually more concise. E.g. compare the old functional style: ``` fn fold_abc(&mut self, abc: ABC) { ABC { a: fold_a(abc.a), b: fold_b(abc.b), c: abc.c, } } ``` with the imperative style: ``` fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) { visit_a(a); visit_b(b); } ``` (The reductions get larger in more complex examples.) Overall, the patch removes over 200 lines of code -- even though the new code has more comments -- and a lot of the remaining lines have fewer characters. Some notes: - The old style used methods called `fold_*`. The new style mostly uses methods called `visit_*`, but there are a few methods that map a `T` to something other than a `T`, which are called `flat_map_*` (`T` maps to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s). - `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed `map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to reflect their slightly changed signatures. - Although this commit renames the `fold` module as `mut_visit`, it keeps it in the `fold.rs` file, so as not to confuse git. The next commit will rename the file.
2019-02-05 15:20:55 +11:00
collector.invocations
};
if self.monotonic {
self.cx
.resolver
.visit_ast_fragment_with_placeholders(self.cx.current_expansion.id, &fragment);
2021-05-02 21:19:28 +02:00
if self.cx.sess.opts.incremental.is_some() {
2021-05-02 21:19:28 +02:00
for (invoc, _) in invocations.iter_mut() {
let expn_id = invoc.expansion_data.id;
let parent_def = self.cx.resolver.invocation_parent(expn_id);
let span = invoc.span_mut();
2021-05-02 21:19:28 +02:00
*span = span.with_parent(Some(parent_def));
}
}
}
Overhaul `syntax::fold::Folder`. This commit changes `syntax::fold::Folder` from a functional style (where most methods take a `T` and produce a new `T`) to a more imperative style (where most methods take and modify a `&mut T`), and renames it `syntax::mut_visit::MutVisitor`. The first benefit is speed. The functional style does not require any reallocations, due to the use of `P::map` and `MoveMap::move_{,flat_}map`. However, every field in the AST must be overwritten; even those fields that are unchanged are overwritten with the same value. This causes a lot of unnecessary memory writes. The imperative style reduces instruction counts by 1--3% across a wide range of workloads, particularly incremental workloads. The second benefit is conciseness; the imperative style is usually more concise. E.g. compare the old functional style: ``` fn fold_abc(&mut self, abc: ABC) { ABC { a: fold_a(abc.a), b: fold_b(abc.b), c: abc.c, } } ``` with the imperative style: ``` fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) { visit_a(a); visit_b(b); } ``` (The reductions get larger in more complex examples.) Overall, the patch removes over 200 lines of code -- even though the new code has more comments -- and a lot of the remaining lines have fewer characters. Some notes: - The old style used methods called `fold_*`. The new style mostly uses methods called `visit_*`, but there are a few methods that map a `T` to something other than a `T`, which are called `flat_map_*` (`T` maps to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s). - `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed `map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to reflect their slightly changed signatures. - Although this commit renames the `fold` module as `mut_visit`, it keeps it in the `fold.rs` file, so as not to confuse git. The next commit will rename the file.
2019-02-05 15:20:55 +11:00
(fragment, invocations)
}
fn error_recursion_limit_reached(&mut self) -> ErrorGuaranteed {
let expn_data = self.cx.current_expansion.id.expn_data();
let suggested_limit = match self.cx.ecfg.recursion_limit {
Limit(0) => Limit(2),
limit => limit * 2,
};
let guar = self.cx.dcx().emit_err(RecursionLimitReached {
span: expn_data.call_site,
descr: expn_data.kind.descr(),
suggested_limit,
crate_name: &self.cx.ecfg.crate_name,
});
self.cx.trace_macros_diag();
guar
}
/// A macro's expansion does not fit in this fragment kind.
/// For example, a non-type macro in a type position.
fn error_wrong_fragment_kind(
&mut self,
kind: AstFragmentKind,
mac: &ast::MacCall,
span: Span,
) -> ErrorGuaranteed {
let guar =
self.cx.dcx().emit_err(WrongFragmentKind { span, kind: kind.name(), name: &mac.path });
self.cx.trace_macros_diag();
guar
}
fn expand_invoc(
&mut self,
invoc: Invocation,
ext: &SyntaxExtensionKind,
) -> ExpandResult<AstFragment, Invocation> {
let recursion_limit = match self.cx.reduced_recursion_limit {
Some((limit, _)) => limit,
None => self.cx.ecfg.recursion_limit,
};
if !recursion_limit.value_within_limit(self.cx.current_expansion.depth) {
let guar = match self.cx.reduced_recursion_limit {
Some((_, guar)) => guar,
None => self.error_recursion_limit_reached(),
};
// Reduce the recursion limit by half each time it triggers.
self.cx.reduced_recursion_limit = Some((recursion_limit / 2, guar));
return ExpandResult::Ready(invoc.fragment_kind.dummy(invoc.span(), guar));
}
2017-03-17 04:04:41 +00:00
let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());
ExpandResult::Ready(match invoc.kind {
InvocationKind::Bang { mac, span } => match ext {
SyntaxExtensionKind::Bang(expander) => {
match expander.expand(self.cx, span, mac.args.tokens.clone()) {
Ok(tok_result) => {
self.parse_ast_fragment(tok_result, fragment_kind, &mac.path, span)
}
Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
}
}
SyntaxExtensionKind::LegacyBang(expander) => {
let tok_result = match expander.expand(self.cx, span, mac.args.tokens.clone()) {
ExpandResult::Ready(tok_result) => tok_result,
ExpandResult::Retry(_) => {
// retry the original
return ExpandResult::Retry(Invocation {
kind: InvocationKind::Bang { mac, span },
..invoc
});
}
};
let result = if let Some(result) = fragment_kind.make_from(tok_result) {
result
} else {
let guar = self.error_wrong_fragment_kind(fragment_kind, &mac, span);
fragment_kind.dummy(span, guar)
};
result
}
_ => unreachable!(),
},
InvocationKind::Attr { attr, pos, mut item, derives } => match ext {
SyntaxExtensionKind::Attr(expander) => {
self.gate_proc_macro_input(&item);
self.gate_proc_macro_attr_item(span, &item);
let tokens = match &item {
// FIXME: Collect tokens and use them instead of generating
// fake ones. These are unstable, so it needs to be
// fixed prior to stabilization
// Fake tokens when we are invoking an inner attribute, and
// we are invoking it on an out-of-line module or crate.
Annotatable::Crate(krate) => {
rustc_parse::fake_token_stream_for_crate(&self.cx.sess.psess, krate)
}
Annotatable::Item(item_inner)
2022-01-07 14:54:16 +08:00
if matches!(attr.style, AttrStyle::Inner)
&& matches!(
item_inner.kind,
ItemKind::Mod(
Move `ast::Item::ident` into `ast::ItemKind`. `ast::Item` has an `ident` field. - It's always non-empty for these item kinds: `ExternCrate`, `Static`, `Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`, `Trait`, `TraitAlias`, `MacroDef`, `Delegation`. - It's always empty for these item kinds: `Use`, `ForeignMod`, `GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`. There is a similar story for `AssocItemKind` and `ForeignItemKind`. Some sites that handle items check for an empty ident, some don't. This is a very C-like way of doing things, but this is Rust, we have sum types, we can do this properly and never forget to check for the exceptional case and never YOLO possibly empty identifiers (or possibly dummy spans) around and hope that things will work out. The commit is large but it's mostly obvious plumbing work. Some notable things. - `ast::Item` got 8 bytes bigger. This could be avoided by boxing the fields within some of the `ast::ItemKind` variants (specifically: `Struct`, `Union`, `Enum`). I might do that in a follow-up; this commit is big enough already. - For the visitors: `FnKind` no longer needs an `ident` field because the `Fn` within how has one. - In the parser, the `ItemInfo` typedef is no longer needed. It was used in various places to return an `Ident` alongside an `ItemKind`, but now the `Ident` (if present) is within the `ItemKind`. - In a few places I renamed identifier variables called `name` (or `foo_name`) as `ident` (or `foo_ident`), to better match the type, and because `name` is normally used for `Symbol`s. It's confusing to see something like `foo_name.name`.
2025-03-21 09:47:43 +11:00
_,
_,
ModKind::Unloaded | ModKind::Loaded(_, Inline::No, _, _),
)
) =>
{
rustc_parse::fake_token_stream_for_item(&self.cx.sess.psess, item_inner)
}
_ => item.to_tokens(),
};
let attr_item = attr.unwrap_normal_item();
if let AttrArgs::Eq { .. } = attr_item.args {
self.cx.dcx().emit_err(UnsupportedKeyValue { span });
}
2020-03-17 10:56:00 +01:00
let inner_tokens = attr_item.args.inner_tokens();
match expander.expand(self.cx, span, inner_tokens, tokens) {
Ok(tok_result) => self.parse_ast_fragment(
tok_result,
fragment_kind,
&attr_item.path,
span,
),
Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
}
}
SyntaxExtensionKind::LegacyAttr(expander) => {
match validate_attr::parse_meta(&self.cx.sess.psess, &attr) {
Ok(meta) => {
2022-09-20 11:55:07 +00:00
let items = match expander.expand(self.cx, span, &meta, item, false) {
ExpandResult::Ready(items) => items,
ExpandResult::Retry(item) => {
// Reassemble the original invocation for retrying.
return ExpandResult::Retry(Invocation {
kind: InvocationKind::Attr { attr, pos, item, derives },
..invoc
});
}
};
if matches!(
fragment_kind,
AstFragmentKind::Expr | AstFragmentKind::MethodReceiverExpr
) && items.is_empty()
{
let guar = self.cx.dcx().emit_err(RemoveExprNotSupported { span });
fragment_kind.dummy(span, guar)
} else {
fragment_kind.expect_from_annotatables(items)
}
}
Make `DiagnosticBuilder::emit` consuming. This works for most of its call sites. This is nice, because `emit` very much makes sense as a consuming operation -- indeed, `DiagnosticBuilderState` exists to ensure no diagnostic is emitted twice, but it uses runtime checks. For the small number of call sites where a consuming emit doesn't work, the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will be removed in subsequent commits.) Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes consuming, while `delay_as_bug_without_consuming` is added (which will also be removed in subsequent commits.) All this requires significant changes to `DiagnosticBuilder`'s chaining methods. Currently `DiagnosticBuilder` method chaining uses a non-consuming `&mut self -> &mut Self` style, which allows chaining to be used when the chain ends in `emit()`, like so: ``` struct_err(msg).span(span).emit(); ``` But it doesn't work when producing a `DiagnosticBuilder` value, requiring this: ``` let mut err = self.struct_err(msg); err.span(span); err ``` This style of chaining won't work with consuming `emit` though. For that, we need to use to a `self -> Self` style. That also would allow `DiagnosticBuilder` production to be chained, e.g.: ``` self.struct_err(msg).span(span) ``` However, removing the `&mut self -> &mut Self` style would require that individual modifications of a `DiagnosticBuilder` go from this: ``` err.span(span); ``` to this: ``` err = err.span(span); ``` There are *many* such places. I have a high tolerance for tedious refactorings, but even I gave up after a long time trying to convert them all. Instead, this commit has it both ways: the existing `&mut self -> Self` chaining methods are kept, and new `self -> Self` chaining methods are added, all of which have a `_mv` suffix (short for "move"). Changes to the existing `forward!` macro lets this happen with very little additional boilerplate code. I chose to add the suffix to the new chaining methods rather than the existing ones, because the number of changes required is much smaller that way. This doubled chainging is a bit clumsy, but I think it is worthwhile because it allows a *lot* of good things to subsequently happen. In this commit, there are many `mut` qualifiers removed in places where diagnostics are emitted without being modified. In subsequent commits: - chaining can be used more, making the code more concise; - more use of chaining also permits the removal of redundant diagnostic APIs like `struct_err_with_code`, which can be replaced easily with `struct_err` + `code_mv`; - `emit_without_diagnostic` can be removed, which simplifies a lot of machinery, removing the need for `DiagnosticBuilderState`.
2024-01-03 12:17:35 +11:00
Err(err) => {
let _guar = err.emit();
fragment_kind.expect_from_annotatables(iter::once(item))
}
}
}
2021-08-05 17:58:59 -05:00
SyntaxExtensionKind::NonMacroAttr => {
self.cx.expanded_inert_attrs.mark(&attr);
item.visit_attrs(|attrs| attrs.insert(pos, attr));
fragment_kind.expect_from_annotatables(iter::once(item))
}
_ => unreachable!(),
},
2022-09-20 11:55:07 +00:00
InvocationKind::Derive { path, item, is_const } => match ext {
SyntaxExtensionKind::Derive(expander)
| SyntaxExtensionKind::LegacyDerive(expander) => {
if let SyntaxExtensionKind::Derive(..) = ext {
self.gate_proc_macro_input(&item);
}
2024-04-23 07:50:53 -05:00
// The `MetaItem` representing the trait to derive can't
// have an unsafe around it (as of now).
2024-04-20 23:54:50 -05:00
let meta = ast::MetaItem {
2024-05-21 08:37:05 -05:00
unsafety: ast::Safety::Default,
2024-04-20 23:54:50 -05:00
kind: MetaItemKind::Word,
span,
path,
};
2022-09-20 11:55:07 +00:00
let items = match expander.expand(self.cx, span, &meta, item, is_const) {
ExpandResult::Ready(items) => items,
ExpandResult::Retry(item) => {
// Reassemble the original invocation for retrying.
return ExpandResult::Retry(Invocation {
2022-09-20 11:55:07 +00:00
kind: InvocationKind::Derive { path: meta.path, item, is_const },
..invoc
});
}
};
fragment_kind.expect_from_annotatables(items)
}
_ => unreachable!(),
},
InvocationKind::GlobDelegation { item, of_trait } => {
2024-03-15 14:21:03 +03:00
let AssocItemKind::DelegationMac(deleg) = &item.kind else { unreachable!() };
let suffixes = match ext {
SyntaxExtensionKind::GlobDelegation(expander) => match expander.expand(self.cx)
{
ExpandResult::Ready(suffixes) => suffixes,
ExpandResult::Retry(()) => {
// Reassemble the original invocation for retrying.
return ExpandResult::Retry(Invocation {
kind: InvocationKind::GlobDelegation { item, of_trait },
2024-03-15 14:21:03 +03:00
..invoc
});
}
},
SyntaxExtensionKind::LegacyBang(..) => {
let msg = "expanded a dummy glob delegation";
let guar = self.cx.dcx().span_delayed_bug(span, msg);
return ExpandResult::Ready(fragment_kind.dummy(span, guar));
}
_ => unreachable!(),
};
type Node = AstNodeWrapper<P<ast::AssocItem>, ImplItemTag>;
let single_delegations = build_single_delegations::<Node>(
self.cx, deleg, &item, &suffixes, item.span, true,
);
fragment_kind.expect_from_annotatables(
single_delegations
.map(|item| Annotatable::AssocItem(P(item), AssocCtxt::Impl { of_trait })),
2024-03-15 14:21:03 +03:00
)
}
})
}
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
rustc: Tweak custom attribute capabilities This commit starts to lay some groundwork for the stabilization of custom attribute invocations and general procedural macros. It applies a number of changes discussed on [internals] as well as a [recent issue][issue], namely: * The path used to specify a custom attribute must be of length one and cannot be a global path. This'll help future-proof us against any ambiguities and give us more time to settle the precise syntax. In the meantime though a bare identifier can be used and imported to invoke a custom attribute macro. A new feature gate, `proc_macro_path_invoc`, was added to gate multi-segment paths and absolute paths. * The set of items which can be annotated by a custom procedural attribute has been restricted. Statements, expressions, and modules are disallowed behind two new feature gates: `proc_macro_expr` and `proc_macro_mod`. * The input to procedural macro attributes has been restricted and adjusted. Today an invocation like `#[foo(bar)]` will receive `(bar)` as the input token stream, but after this PR it will only receive `bar` (the delimiters were removed). Invocations like `#[foo]` are still allowed and will be invoked in the same way as `#[foo()]`. This is a **breaking change** for all nightly users as the syntax coming in to procedural macros will be tweaked slightly. * Procedural macros (`foo!()` style) can only be expanded to item-like items by default. A separate feature gate, `proc_macro_non_items`, is required to expand to items like expressions, statements, etc. Closes #50038 [internals]: https://internals.rust-lang.org/t/help-stabilize-a-subset-of-macros-2-0/7252 [issue]: https://github.com/rust-lang/rust/issues/50038
2018-04-20 07:50:39 -07:00
fn gate_proc_macro_attr_item(&self, span: Span, item: &Annotatable) {
let kind = match item {
Annotatable::Item(_)
| Annotatable::AssocItem(..)
| Annotatable::ForeignItem(_)
| Annotatable::Crate(..) => return,
Annotatable::Stmt(stmt) => {
// Attributes are stable on item statements,
// but unstable on all other kinds of statements
if stmt.is_item() {
return;
}
"statements"
}
Annotatable::Expr(_) => "expressions",
Annotatable::Arm(..)
| Annotatable::ExprField(..)
| Annotatable::PatField(..)
| Annotatable::GenericParam(..)
| Annotatable::Param(..)
| Annotatable::FieldDef(..)
| Annotatable::Variant(..)
| Annotatable::WherePredicate(..) => panic!("unexpected annotatable"),
rustc: Tweak custom attribute capabilities This commit starts to lay some groundwork for the stabilization of custom attribute invocations and general procedural macros. It applies a number of changes discussed on [internals] as well as a [recent issue][issue], namely: * The path used to specify a custom attribute must be of length one and cannot be a global path. This'll help future-proof us against any ambiguities and give us more time to settle the precise syntax. In the meantime though a bare identifier can be used and imported to invoke a custom attribute macro. A new feature gate, `proc_macro_path_invoc`, was added to gate multi-segment paths and absolute paths. * The set of items which can be annotated by a custom procedural attribute has been restricted. Statements, expressions, and modules are disallowed behind two new feature gates: `proc_macro_expr` and `proc_macro_mod`. * The input to procedural macro attributes has been restricted and adjusted. Today an invocation like `#[foo(bar)]` will receive `(bar)` as the input token stream, but after this PR it will only receive `bar` (the delimiters were removed). Invocations like `#[foo]` are still allowed and will be invoked in the same way as `#[foo()]`. This is a **breaking change** for all nightly users as the syntax coming in to procedural macros will be tweaked slightly. * Procedural macros (`foo!()` style) can only be expanded to item-like items by default. A separate feature gate, `proc_macro_non_items`, is required to expand to items like expressions, statements, etc. Closes #50038 [internals]: https://internals.rust-lang.org/t/help-stabilize-a-subset-of-macros-2-0/7252 [issue]: https://github.com/rust-lang/rust/issues/50038
2018-04-20 07:50:39 -07:00
};
if self.cx.ecfg.features.proc_macro_hygiene() {
return;
}
feature_err(
&self.cx.sess,
sym::proc_macro_hygiene,
rustc: Tweak custom attribute capabilities This commit starts to lay some groundwork for the stabilization of custom attribute invocations and general procedural macros. It applies a number of changes discussed on [internals] as well as a [recent issue][issue], namely: * The path used to specify a custom attribute must be of length one and cannot be a global path. This'll help future-proof us against any ambiguities and give us more time to settle the precise syntax. In the meantime though a bare identifier can be used and imported to invoke a custom attribute macro. A new feature gate, `proc_macro_path_invoc`, was added to gate multi-segment paths and absolute paths. * The set of items which can be annotated by a custom procedural attribute has been restricted. Statements, expressions, and modules are disallowed behind two new feature gates: `proc_macro_expr` and `proc_macro_mod`. * The input to procedural macro attributes has been restricted and adjusted. Today an invocation like `#[foo(bar)]` will receive `(bar)` as the input token stream, but after this PR it will only receive `bar` (the delimiters were removed). Invocations like `#[foo]` are still allowed and will be invoked in the same way as `#[foo()]`. This is a **breaking change** for all nightly users as the syntax coming in to procedural macros will be tweaked slightly. * Procedural macros (`foo!()` style) can only be expanded to item-like items by default. A separate feature gate, `proc_macro_non_items`, is required to expand to items like expressions, statements, etc. Closes #50038 [internals]: https://internals.rust-lang.org/t/help-stabilize-a-subset-of-macros-2-0/7252 [issue]: https://github.com/rust-lang/rust/issues/50038
2018-04-20 07:50:39 -07:00
span,
format!("custom attributes cannot be applied to {kind}"),
)
.emit();
rustc: Tweak custom attribute capabilities This commit starts to lay some groundwork for the stabilization of custom attribute invocations and general procedural macros. It applies a number of changes discussed on [internals] as well as a [recent issue][issue], namely: * The path used to specify a custom attribute must be of length one and cannot be a global path. This'll help future-proof us against any ambiguities and give us more time to settle the precise syntax. In the meantime though a bare identifier can be used and imported to invoke a custom attribute macro. A new feature gate, `proc_macro_path_invoc`, was added to gate multi-segment paths and absolute paths. * The set of items which can be annotated by a custom procedural attribute has been restricted. Statements, expressions, and modules are disallowed behind two new feature gates: `proc_macro_expr` and `proc_macro_mod`. * The input to procedural macro attributes has been restricted and adjusted. Today an invocation like `#[foo(bar)]` will receive `(bar)` as the input token stream, but after this PR it will only receive `bar` (the delimiters were removed). Invocations like `#[foo]` are still allowed and will be invoked in the same way as `#[foo()]`. This is a **breaking change** for all nightly users as the syntax coming in to procedural macros will be tweaked slightly. * Procedural macros (`foo!()` style) can only be expanded to item-like items by default. A separate feature gate, `proc_macro_non_items`, is required to expand to items like expressions, statements, etc. Closes #50038 [internals]: https://internals.rust-lang.org/t/help-stabilize-a-subset-of-macros-2-0/7252 [issue]: https://github.com/rust-lang/rust/issues/50038
2018-04-20 07:50:39 -07:00
}
fn gate_proc_macro_input(&self, annotatable: &Annotatable) {
struct GateProcMacroInput<'a> {
sess: &'a Session,
}
impl<'ast, 'a> Visitor<'ast> for GateProcMacroInput<'a> {
fn visit_item(&mut self, item: &'ast ast::Item) {
match &item.kind {
Move `ast::Item::ident` into `ast::ItemKind`. `ast::Item` has an `ident` field. - It's always non-empty for these item kinds: `ExternCrate`, `Static`, `Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`, `Trait`, `TraitAlias`, `MacroDef`, `Delegation`. - It's always empty for these item kinds: `Use`, `ForeignMod`, `GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`. There is a similar story for `AssocItemKind` and `ForeignItemKind`. Some sites that handle items check for an empty ident, some don't. This is a very C-like way of doing things, but this is Rust, we have sum types, we can do this properly and never forget to check for the exceptional case and never YOLO possibly empty identifiers (or possibly dummy spans) around and hope that things will work out. The commit is large but it's mostly obvious plumbing work. Some notable things. - `ast::Item` got 8 bytes bigger. This could be avoided by boxing the fields within some of the `ast::ItemKind` variants (specifically: `Struct`, `Union`, `Enum`). I might do that in a follow-up; this commit is big enough already. - For the visitors: `FnKind` no longer needs an `ident` field because the `Fn` within how has one. - In the parser, the `ItemInfo` typedef is no longer needed. It was used in various places to return an `Ident` alongside an `ItemKind`, but now the `Ident` (if present) is within the `ItemKind`. - In a few places I renamed identifier variables called `name` (or `foo_name`) as `ident` (or `foo_ident`), to better match the type, and because `name` is normally used for `Symbol`s. It's confusing to see something like `foo_name.name`.
2025-03-21 09:47:43 +11:00
ItemKind::Mod(_, _, mod_kind)
if !matches!(mod_kind, ModKind::Loaded(_, Inline::Yes, _, _)) =>
{
feature_err(
self.sess,
sym::proc_macro_hygiene,
item.span,
fluent_generated::expand_non_inline_modules_in_proc_macro_input_are_unstable,
)
.emit();
}
_ => {}
}
visit::walk_item(self, item);
}
}
if !self.cx.ecfg.features.proc_macro_hygiene() {
annotatable.visit_with(&mut GateProcMacroInput { sess: &self.cx.sess });
}
}
fn parse_ast_fragment(
&mut self,
toks: TokenStream,
kind: AstFragmentKind,
2022-01-07 14:54:16 +08:00
path: &ast::Path,
span: Span,
) -> AstFragment {
let mut parser = self.cx.new_parser_from_tts(toks);
2019-12-03 18:47:44 +01:00
match parse_ast_fragment(&mut parser, kind) {
Ok(fragment) => {
compiler: fix few needless_pass_by_ref_mut clippy lints warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_session\src\config.rs:2013:16 | 2013 | early_dcx: &mut EarlyDiagCtxt, | ^^^^^^^^^^^^^^^^^^ help: consider changing to: `&EarlyDiagCtxt` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_ast_passes\src\ast_validation.rs:1555:11 | 1555 | this: &mut AstValidator<'_>, | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&AstValidator<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_infer\src\infer\snapshot\fudge.rs:16:12 | 16 | table: &mut UnificationTable<'_, 'tcx, T>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&UnificationTable<'_, 'tcx, T>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_expand\src\expand.rs:961:13 | 961 | parser: &mut Parser<'a>, | ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'a>` | = warning: changing this function will impact semver compatibility = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
2024-03-28 11:30:49 +03:00
ensure_complete_parse(&parser, path, kind.name(), span);
fragment
}
2016-09-26 04:16:55 +00:00
Err(mut err) => {
if err.span.is_dummy() {
err.span(span);
}
annotate_err_with_kind(&mut err, kind, span);
let guar = err.emit();
self.cx.trace_macros_diag();
kind.dummy(span, guar)
2016-09-26 04:16:55 +00:00
}
}
2016-09-26 04:16:55 +00:00
}
2016-09-02 09:12:47 +00:00
}
pub fn parse_ast_fragment<'a>(
this: &mut Parser<'a>,
kind: AstFragmentKind,
) -> PResult<'a, AstFragment> {
Ok(match kind {
AstFragmentKind::Items => {
let mut items = SmallVec::new();
while let Some(item) = this.parse_item(ForceCollect::No)? {
items.push(item);
}
AstFragment::Items(items)
}
AstFragmentKind::TraitItems => {
let mut items = SmallVec::new();
Implement token-based handling of attributes during expansion This PR modifies the macro expansion infrastructure to handle attributes in a fully token-based manner. As a result: * Derives macros no longer lose spans when their input is modified by eager cfg-expansion. This is accomplished by performing eager cfg-expansion on the token stream that we pass to the derive proc-macro * Inner attributes now preserve spans in all cases, including when we have multiple inner attributes in a row. This is accomplished through the following changes: * New structs `AttrAnnotatedTokenStream` and `AttrAnnotatedTokenTree` are introduced. These are very similar to a normal `TokenTree`, but they also track the position of attributes and attribute targets within the stream. They are built when we collect tokens during parsing. An `AttrAnnotatedTokenStream` is converted to a regular `TokenStream` when we invoke a macro. * Token capturing and `LazyTokenStream` are modified to work with `AttrAnnotatedTokenStream`. A new `ReplaceRange` type is introduced, which is created during the parsing of a nested AST node to make the 'outer' AST node aware of the attributes and attribute target stored deeper in the token stream. * When we need to perform eager cfg-expansion (either due to `#[derive]` or `#[cfg_eval]`), we tokenize and reparse our target, capturing additional information about the locations of `#[cfg]` and `#[cfg_attr]` attributes at any depth within the target. This is a performance optimization, allowing us to perform less work in the typical case where captured tokens never have eager cfg-expansion run.
2020-11-28 18:33:17 -05:00
while let Some(item) = this.parse_trait_item(ForceCollect::No)? {
items.extend(item);
}
AstFragment::TraitItems(items)
}
AstFragmentKind::ImplItems => {
let mut items = SmallVec::new();
Implement token-based handling of attributes during expansion This PR modifies the macro expansion infrastructure to handle attributes in a fully token-based manner. As a result: * Derives macros no longer lose spans when their input is modified by eager cfg-expansion. This is accomplished by performing eager cfg-expansion on the token stream that we pass to the derive proc-macro * Inner attributes now preserve spans in all cases, including when we have multiple inner attributes in a row. This is accomplished through the following changes: * New structs `AttrAnnotatedTokenStream` and `AttrAnnotatedTokenTree` are introduced. These are very similar to a normal `TokenTree`, but they also track the position of attributes and attribute targets within the stream. They are built when we collect tokens during parsing. An `AttrAnnotatedTokenStream` is converted to a regular `TokenStream` when we invoke a macro. * Token capturing and `LazyTokenStream` are modified to work with `AttrAnnotatedTokenStream`. A new `ReplaceRange` type is introduced, which is created during the parsing of a nested AST node to make the 'outer' AST node aware of the attributes and attribute target stored deeper in the token stream. * When we need to perform eager cfg-expansion (either due to `#[derive]` or `#[cfg_eval]`), we tokenize and reparse our target, capturing additional information about the locations of `#[cfg]` and `#[cfg_attr]` attributes at any depth within the target. This is a performance optimization, allowing us to perform less work in the typical case where captured tokens never have eager cfg-expansion run.
2020-11-28 18:33:17 -05:00
while let Some(item) = this.parse_impl_item(ForceCollect::No)? {
items.extend(item);
}
AstFragment::ImplItems(items)
}
AstFragmentKind::TraitImplItems => {
let mut items = SmallVec::new();
while let Some(item) = this.parse_impl_item(ForceCollect::No)? {
items.extend(item);
}
AstFragment::TraitImplItems(items)
}
AstFragmentKind::ForeignItems => {
let mut items = SmallVec::new();
Implement token-based handling of attributes during expansion This PR modifies the macro expansion infrastructure to handle attributes in a fully token-based manner. As a result: * Derives macros no longer lose spans when their input is modified by eager cfg-expansion. This is accomplished by performing eager cfg-expansion on the token stream that we pass to the derive proc-macro * Inner attributes now preserve spans in all cases, including when we have multiple inner attributes in a row. This is accomplished through the following changes: * New structs `AttrAnnotatedTokenStream` and `AttrAnnotatedTokenTree` are introduced. These are very similar to a normal `TokenTree`, but they also track the position of attributes and attribute targets within the stream. They are built when we collect tokens during parsing. An `AttrAnnotatedTokenStream` is converted to a regular `TokenStream` when we invoke a macro. * Token capturing and `LazyTokenStream` are modified to work with `AttrAnnotatedTokenStream`. A new `ReplaceRange` type is introduced, which is created during the parsing of a nested AST node to make the 'outer' AST node aware of the attributes and attribute target stored deeper in the token stream. * When we need to perform eager cfg-expansion (either due to `#[derive]` or `#[cfg_eval]`), we tokenize and reparse our target, capturing additional information about the locations of `#[cfg]` and `#[cfg_attr]` attributes at any depth within the target. This is a performance optimization, allowing us to perform less work in the typical case where captured tokens never have eager cfg-expansion run.
2020-11-28 18:33:17 -05:00
while let Some(item) = this.parse_foreign_item(ForceCollect::No)? {
items.extend(item);
}
AstFragment::ForeignItems(items)
}
AstFragmentKind::Stmts => {
let mut stmts = SmallVec::new();
2019-12-03 18:47:44 +01:00
// Won't make progress on a `}`.
while this.token != token::Eof && this.token != token::CloseDelim(Delimiter::Brace) {
if let Some(stmt) = this.parse_full_stmt(AttemptLocalParseRecovery::Yes)? {
stmts.push(stmt);
}
}
AstFragment::Stmts(stmts)
}
AstFragmentKind::Expr => AstFragment::Expr(this.parse_expr()?),
AstFragmentKind::MethodReceiverExpr => AstFragment::MethodReceiverExpr(this.parse_expr()?),
AstFragmentKind::OptExpr => {
if this.token != token::Eof {
AstFragment::OptExpr(Some(this.parse_expr()?))
} else {
AstFragment::OptExpr(None)
}
}
AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?),
AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_guard(
None,
RecoverComma::No,
RecoverColon::Yes,
CommaRecoveryMode::LikelyTuple,
)?),
AstFragmentKind::Crate => AstFragment::Crate(this.parse_crate_mod()?),
AstFragmentKind::Arms
| AstFragmentKind::ExprFields
| AstFragmentKind::PatFields
| AstFragmentKind::GenericParams
| AstFragmentKind::Params
| AstFragmentKind::FieldDefs
| AstFragmentKind::Variants
| AstFragmentKind::WherePredicates => panic!("unexpected AST fragment kind"),
})
}
2024-05-01 09:39:07 +10:00
pub(crate) fn ensure_complete_parse<'a>(
compiler: fix few needless_pass_by_ref_mut clippy lints warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_session\src\config.rs:2013:16 | 2013 | early_dcx: &mut EarlyDiagCtxt, | ^^^^^^^^^^^^^^^^^^ help: consider changing to: `&EarlyDiagCtxt` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_ast_passes\src\ast_validation.rs:1555:11 | 1555 | this: &mut AstValidator<'_>, | ^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&AstValidator<'_>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_infer\src\infer\snapshot\fudge.rs:16:12 | 16 | table: &mut UnificationTable<'_, 'tcx, T>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&UnificationTable<'_, 'tcx, T>` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut warning: this argument is a mutable reference, but not used mutably --> compiler\rustc_expand\src\expand.rs:961:13 | 961 | parser: &mut Parser<'a>, | ^^^^^^^^^^^^^^^ help: consider changing to: `&Parser<'a>` | = warning: changing this function will impact semver compatibility = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut
2024-03-28 11:30:49 +03:00
parser: &Parser<'a>,
2022-01-07 14:54:16 +08:00
macro_path: &ast::Path,
kind_name: &str,
span: Span,
) {
if parser.token != token::Eof {
let descr = token_descr(&parser.token);
// Avoid emitting backtrace info twice.
let def_site_span = parser.token.span.with_ctxt(SyntaxContext::root());
let semi_span = parser.psess.source_map().next_point(span);
let add_semicolon = match &parser.psess.source_map().span_to_snippet(semi_span) {
2022-12-23 17:34:23 +00:00
Ok(snippet) if &snippet[..] != ";" && kind_name == "expression" => {
Some(span.shrink_to_hi())
}
_ => None,
};
let expands_to_match_arm = kind_name == "pattern" && parser.token == token::FatArrow;
parser.dcx().emit_err(IncompleteParse {
span: def_site_span,
descr,
label_span: span,
macro_path,
kind_name,
expands_to_match_arm,
add_semicolon,
});
}
2016-09-02 09:12:47 +00:00
}
/// Wraps a call to `walk_*` / `walk_flat_map_*`
/// for an AST node that supports attributes
/// (see the `Annotatable` enum)
/// This method assigns a `NodeId`, and sets that `NodeId`
/// as our current 'lint node id'. If a macro call is found
/// inside this AST node, we will use this AST node's `NodeId`
/// to emit lints associated with that macro (allowing
/// `#[allow]` / `#[deny]` to be applied close to
/// the macro invocation).
///
/// Do *not* call this for a macro AST node
/// (e.g. `ExprKind::MacCall`) - we cannot emit lints
/// at these AST nodes, since they are removed and
/// replaced with the result of macro expansion.
///
/// All other `NodeId`s are assigned by `visit_id`.
/// * `self` is the 'self' parameter for the current method,
/// * `id` is a mutable reference to the `NodeId` field
/// of the current AST node.
/// * `closure` is a closure that executes the
/// `walk_*` / `walk_flat_map_*` method
/// for the current AST node.
macro_rules! assign_id {
($self:ident, $id:expr, $closure:expr) => {{
let old_id = $self.cx.current_expansion.lint_node_id;
if $self.monotonic {
debug_assert_eq!(*$id, ast::DUMMY_NODE_ID);
let new_id = $self.cx.resolver.next_node_id();
*$id = new_id;
$self.cx.current_expansion.lint_node_id = new_id;
}
let ret = ($closure)();
$self.cx.current_expansion.lint_node_id = old_id;
ret
}};
}
enum AddSemicolon {
Yes,
No,
}
/// A trait implemented for all `AstFragment` nodes and providing all pieces
/// of functionality used by `InvocationCollector`.
trait InvocationCollectorNode: HasAttrs + HasNodeId + Sized {
type OutputTy = SmallVec<[Self; 1]>;
type AttrsTy: Deref<Target = [ast::Attribute]> = ast::AttrVec;
type ItemKind = ItemKind;
const KIND: AstFragmentKind;
fn to_annotatable(self) -> Annotatable;
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy;
fn descr() -> &'static str {
unreachable!()
}
fn walk_flat_map<V: MutVisitor>(self, _visitor: &mut V) -> Self::OutputTy {
unreachable!()
}
fn walk<V: MutVisitor>(&mut self, _visitor: &mut V) {
unreachable!()
}
fn is_mac_call(&self) -> bool {
false
}
2022-08-12 12:20:10 +10:00
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
unreachable!()
}
2024-03-15 14:21:03 +03:00
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
None
}
fn delegation_item_kind(_deleg: Box<ast::Delegation>) -> Self::ItemKind {
unreachable!()
}
fn from_item(_item: ast::Item<Self::ItemKind>) -> Self {
unreachable!()
}
fn flatten_outputs(_outputs: impl Iterator<Item = Self::OutputTy>) -> Self::OutputTy {
unreachable!()
}
fn pre_flat_map_node_collect_attr(_cfg: &StripUnconfigured<'_>, _attr: &ast::Attribute) {}
fn post_flat_map_node_collect_bang(_output: &mut Self::OutputTy, _add_semicolon: AddSemicolon) {
}
fn wrap_flat_map_node_walk_flat_map(
node: Self,
collector: &mut InvocationCollector<'_, '_>,
walk_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy,
) -> Result<Self::OutputTy, Self> {
Ok(walk_flat_map(node, collector))
}
fn expand_cfg_false(
&mut self,
collector: &mut InvocationCollector<'_, '_>,
_pos: usize,
span: Span,
) {
collector.cx.dcx().emit_err(RemoveNodeNotSupported { span, descr: Self::descr() });
}
/// All of the names (items) declared by this node.
/// This is an approximation and should only be used for diagnostics.
fn declared_names(&self) -> Vec<Ident> {
vec![]
}
}
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 walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_item(visitor, self)
}
fn is_mac_call(&self) -> bool {
2022-01-07 14:54:16 +08:00
matches!(self.kind, ItemKind::MacCall(..))
}
2022-08-12 12:20:10 +10:00
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
let node = self.into_inner();
match node.kind {
2022-01-07 14:54:16 +08:00
ItemKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No),
_ => unreachable!(),
}
}
2024-03-15 14:21:03 +03:00
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
match &self.kind {
ItemKind::DelegationMac(deleg) => Some((deleg, self)),
_ => None,
}
}
fn delegation_item_kind(deleg: Box<ast::Delegation>) -> Self::ItemKind {
ItemKind::Delegation(deleg)
}
fn from_item(item: ast::Item<Self::ItemKind>) -> Self {
P(item)
}
fn flatten_outputs(items: impl Iterator<Item = Self::OutputTy>) -> Self::OutputTy {
items.flatten().collect()
}
fn wrap_flat_map_node_walk_flat_map(
mut node: Self,
collector: &mut InvocationCollector<'_, '_>,
walk_flat_map: impl FnOnce(Self, &mut InvocationCollector<'_, '_>) -> Self::OutputTy,
) -> Result<Self::OutputTy, Self> {
2022-01-07 14:54:16 +08:00
if !matches!(node.kind, ItemKind::Mod(..)) {
return Ok(walk_flat_map(node, collector));
}
// Work around borrow checker not seeing through `P`'s deref.
Move `ast::Item::ident` into `ast::ItemKind`. `ast::Item` has an `ident` field. - It's always non-empty for these item kinds: `ExternCrate`, `Static`, `Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`, `Trait`, `TraitAlias`, `MacroDef`, `Delegation`. - It's always empty for these item kinds: `Use`, `ForeignMod`, `GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`. There is a similar story for `AssocItemKind` and `ForeignItemKind`. Some sites that handle items check for an empty ident, some don't. This is a very C-like way of doing things, but this is Rust, we have sum types, we can do this properly and never forget to check for the exceptional case and never YOLO possibly empty identifiers (or possibly dummy spans) around and hope that things will work out. The commit is large but it's mostly obvious plumbing work. Some notable things. - `ast::Item` got 8 bytes bigger. This could be avoided by boxing the fields within some of the `ast::ItemKind` variants (specifically: `Struct`, `Union`, `Enum`). I might do that in a follow-up; this commit is big enough already. - For the visitors: `FnKind` no longer needs an `ident` field because the `Fn` within how has one. - In the parser, the `ItemInfo` typedef is no longer needed. It was used in various places to return an `Ident` alongside an `ItemKind`, but now the `Ident` (if present) is within the `ItemKind`. - In a few places I renamed identifier variables called `name` (or `foo_name`) as `ident` (or `foo_ident`), to better match the type, and because `name` is normally used for `Symbol`s. It's confusing to see something like `foo_name.name`.
2025-03-21 09:47:43 +11:00
let (span, mut attrs) = (node.span, mem::take(&mut node.attrs));
2025-04-01 14:49:58 +11:00
let ItemKind::Mod(_, ident, ref mut mod_kind) = node.kind else { unreachable!() };
let ecx = &mut collector.cx;
let (file_path, dir_path, dir_ownership) = match mod_kind {
ModKind::Loaded(_, inline, _, _) => {
// Inline `mod foo { ... }`, but we still need to push directories.
let (dir_path, dir_ownership) = mod_dir_path(
ecx.sess,
ident,
&attrs,
&ecx.current_expansion.module,
ecx.current_expansion.dir_ownership,
*inline,
);
// If the module was parsed from an external file, recover its path.
// This lets `parse_external_mod` catch cycles if it's self-referential.
let file_path = match inline {
Inline::Yes => None,
Inline::No => mod_file_path_from_attr(ecx.sess, &attrs, &dir_path),
};
node.attrs = attrs;
(file_path, dir_path, dir_ownership)
}
ModKind::Unloaded => {
// We have an outline `mod foo;` so we need to parse the file.
let old_attrs_len = attrs.len();
let ParsedExternalMod {
items,
spans,
file_path,
dir_path,
dir_ownership,
had_parse_error,
} = 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.ecfg.features,
ecx.resolver.registered_tools(),
ecx.current_expansion.lint_node_id,
&attrs,
&items,
ident.name,
);
}
*mod_kind = ModKind::Loaded(items, Inline::No, spans, had_parse_error);
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(walk_flat_map(node, collector));
collector.cx.current_expansion.dir_ownership = orig_dir_ownership;
collector.cx.current_expansion.module = orig_module;
res
}
Move `ast::Item::ident` into `ast::ItemKind`. `ast::Item` has an `ident` field. - It's always non-empty for these item kinds: `ExternCrate`, `Static`, `Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`, `Trait`, `TraitAlias`, `MacroDef`, `Delegation`. - It's always empty for these item kinds: `Use`, `ForeignMod`, `GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`. There is a similar story for `AssocItemKind` and `ForeignItemKind`. Some sites that handle items check for an empty ident, some don't. This is a very C-like way of doing things, but this is Rust, we have sum types, we can do this properly and never forget to check for the exceptional case and never YOLO possibly empty identifiers (or possibly dummy spans) around and hope that things will work out. The commit is large but it's mostly obvious plumbing work. Some notable things. - `ast::Item` got 8 bytes bigger. This could be avoided by boxing the fields within some of the `ast::ItemKind` variants (specifically: `Struct`, `Union`, `Enum`). I might do that in a follow-up; this commit is big enough already. - For the visitors: `FnKind` no longer needs an `ident` field because the `Fn` within how has one. - In the parser, the `ItemInfo` typedef is no longer needed. It was used in various places to return an `Ident` alongside an `ItemKind`, but now the `Ident` (if present) is within the `ItemKind`. - In a few places I renamed identifier variables called `name` (or `foo_name`) as `ident` (or `foo_ident`), to better match the type, and because `name` is normally used for `Symbol`s. It's confusing to see something like `foo_name.name`.
2025-03-21 09:47:43 +11:00
fn declared_names(&self) -> Vec<Ident> {
if let ItemKind::Use(ut) = &self.kind {
fn collect_use_tree_leaves(ut: &ast::UseTree, idents: &mut Vec<Ident>) {
match &ut.kind {
ast::UseTreeKind::Glob => {}
ast::UseTreeKind::Simple(_) => idents.push(ut.ident()),
ast::UseTreeKind::Nested { items, .. } => {
for (ut, _) in items {
collect_use_tree_leaves(ut, idents);
}
}
}
}
let mut idents = Vec::new();
collect_use_tree_leaves(ut, &mut idents);
return idents;
}
Move `ast::Item::ident` into `ast::ItemKind`. `ast::Item` has an `ident` field. - It's always non-empty for these item kinds: `ExternCrate`, `Static`, `Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`, `Trait`, `TraitAlias`, `MacroDef`, `Delegation`. - It's always empty for these item kinds: `Use`, `ForeignMod`, `GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`. There is a similar story for `AssocItemKind` and `ForeignItemKind`. Some sites that handle items check for an empty ident, some don't. This is a very C-like way of doing things, but this is Rust, we have sum types, we can do this properly and never forget to check for the exceptional case and never YOLO possibly empty identifiers (or possibly dummy spans) around and hope that things will work out. The commit is large but it's mostly obvious plumbing work. Some notable things. - `ast::Item` got 8 bytes bigger. This could be avoided by boxing the fields within some of the `ast::ItemKind` variants (specifically: `Struct`, `Union`, `Enum`). I might do that in a follow-up; this commit is big enough already. - For the visitors: `FnKind` no longer needs an `ident` field because the `Fn` within how has one. - In the parser, the `ItemInfo` typedef is no longer needed. It was used in various places to return an `Ident` alongside an `ItemKind`, but now the `Ident` (if present) is within the `ItemKind`. - In a few places I renamed identifier variables called `name` (or `foo_name`) as `ident` (or `foo_ident`), to better match the type, and because `name` is normally used for `Symbol`s. It's confusing to see something like `foo_name.name`.
2025-03-21 09:47:43 +11:00
if let Some(ident) = self.kind.ident() { vec![ident] } else { vec![] }
}
}
struct TraitItemTag;
impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitItemTag> {
type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>;
type ItemKind = AssocItemKind;
const KIND: AstFragmentKind = AstFragmentKind::TraitItems;
fn to_annotatable(self) -> Annotatable {
Annotatable::AssocItem(self.wrapped, AssocCtxt::Trait)
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
fragment.make_trait_items()
}
fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_assoc_item(visitor, self.wrapped, AssocCtxt::Trait)
}
fn is_mac_call(&self) -> bool {
2022-01-07 14:54:16 +08:00
matches!(self.wrapped.kind, AssocItemKind::MacCall(..))
}
2022-08-12 12:20:10 +10:00
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
let item = self.wrapped.into_inner();
match item.kind {
2022-01-07 14:54:16 +08:00
AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No),
_ => unreachable!(),
}
}
2024-03-15 14:21:03 +03:00
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
match &self.wrapped.kind {
AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)),
_ => None,
}
}
fn delegation_item_kind(deleg: Box<ast::Delegation>) -> Self::ItemKind {
AssocItemKind::Delegation(deleg)
}
fn from_item(item: ast::Item<Self::ItemKind>) -> Self {
AstNodeWrapper::new(P(item), TraitItemTag)
}
fn flatten_outputs(items: impl Iterator<Item = Self::OutputTy>) -> Self::OutputTy {
items.flatten().collect()
}
}
struct ImplItemTag;
impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, ImplItemTag> {
type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>;
type ItemKind = AssocItemKind;
const KIND: AstFragmentKind = AstFragmentKind::ImplItems;
fn to_annotatable(self) -> Annotatable {
Annotatable::AssocItem(self.wrapped, AssocCtxt::Impl { of_trait: false })
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
fragment.make_impl_items()
}
fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_assoc_item(visitor, self.wrapped, AssocCtxt::Impl { of_trait: false })
}
fn is_mac_call(&self) -> bool {
2022-01-07 14:54:16 +08:00
matches!(self.wrapped.kind, AssocItemKind::MacCall(..))
}
2022-08-12 12:20:10 +10:00
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
let item = self.wrapped.into_inner();
match item.kind {
2022-01-07 14:54:16 +08:00
AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No),
_ => unreachable!(),
}
}
2024-03-15 14:21:03 +03:00
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
match &self.wrapped.kind {
AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)),
_ => None,
}
}
fn delegation_item_kind(deleg: Box<ast::Delegation>) -> Self::ItemKind {
AssocItemKind::Delegation(deleg)
}
fn from_item(item: ast::Item<Self::ItemKind>) -> Self {
AstNodeWrapper::new(P(item), ImplItemTag)
}
fn flatten_outputs(items: impl Iterator<Item = Self::OutputTy>) -> Self::OutputTy {
items.flatten().collect()
}
}
struct TraitImplItemTag;
impl InvocationCollectorNode for AstNodeWrapper<P<ast::AssocItem>, TraitImplItemTag> {
type OutputTy = SmallVec<[P<ast::AssocItem>; 1]>;
type ItemKind = AssocItemKind;
const KIND: AstFragmentKind = AstFragmentKind::TraitImplItems;
fn to_annotatable(self) -> Annotatable {
Annotatable::AssocItem(self.wrapped, AssocCtxt::Impl { of_trait: true })
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
fragment.make_trait_impl_items()
}
fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_assoc_item(visitor, self.wrapped, AssocCtxt::Impl { of_trait: true })
}
fn is_mac_call(&self) -> bool {
matches!(self.wrapped.kind, AssocItemKind::MacCall(..))
}
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
let item = self.wrapped.into_inner();
match item.kind {
AssocItemKind::MacCall(mac) => (mac, item.attrs, AddSemicolon::No),
_ => unreachable!(),
}
}
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
match &self.wrapped.kind {
AssocItemKind::DelegationMac(deleg) => Some((deleg, &self.wrapped)),
_ => None,
}
}
fn delegation_item_kind(deleg: Box<ast::Delegation>) -> Self::ItemKind {
AssocItemKind::Delegation(deleg)
}
fn from_item(item: ast::Item<Self::ItemKind>) -> Self {
AstNodeWrapper::new(P(item), TraitImplItemTag)
}
fn flatten_outputs(items: impl Iterator<Item = Self::OutputTy>) -> Self::OutputTy {
items.flatten().collect()
}
}
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 walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
2024-11-17 20:11:49 -03:00
walk_flat_map_foreign_item(visitor, self)
}
fn is_mac_call(&self) -> bool {
2022-01-07 14:54:16 +08:00
matches!(self.kind, ForeignItemKind::MacCall(..))
}
2022-08-12 12:20:10 +10:00
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
let node = self.into_inner();
match node.kind {
2022-01-07 14:54:16 +08:00
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 walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_variant(visitor, self)
}
}
impl InvocationCollectorNode for ast::WherePredicate {
const KIND: AstFragmentKind = AstFragmentKind::WherePredicates;
fn to_annotatable(self) -> Annotatable {
Annotatable::WherePredicate(self)
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
fragment.make_where_predicates()
}
fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_where_predicate(visitor, self)
}
}
impl InvocationCollectorNode for ast::FieldDef {
const KIND: AstFragmentKind = AstFragmentKind::FieldDefs;
fn to_annotatable(self) -> Annotatable {
Annotatable::FieldDef(self)
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
fragment.make_field_defs()
}
fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_field_def(visitor, self)
}
}
impl InvocationCollectorNode for ast::PatField {
const KIND: AstFragmentKind = AstFragmentKind::PatFields;
fn to_annotatable(self) -> Annotatable {
Annotatable::PatField(self)
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
fragment.make_pat_fields()
}
fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_pat_field(visitor, self)
}
}
impl InvocationCollectorNode for ast::ExprField {
const KIND: AstFragmentKind = AstFragmentKind::ExprFields;
fn to_annotatable(self) -> Annotatable {
Annotatable::ExprField(self)
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
fragment.make_expr_fields()
}
fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_expr_field(visitor, self)
}
}
impl InvocationCollectorNode for ast::Param {
const KIND: AstFragmentKind = AstFragmentKind::Params;
fn to_annotatable(self) -> Annotatable {
Annotatable::Param(self)
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
fragment.make_params()
}
fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_param(visitor, self)
}
}
impl InvocationCollectorNode for ast::GenericParam {
const KIND: AstFragmentKind = AstFragmentKind::GenericParams;
fn to_annotatable(self) -> Annotatable {
Annotatable::GenericParam(self)
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
fragment.make_generic_params()
}
fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_generic_param(visitor, self)
}
}
impl InvocationCollectorNode for ast::Arm {
const KIND: AstFragmentKind = AstFragmentKind::Arms;
fn to_annotatable(self) -> Annotatable {
Annotatable::Arm(self)
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
fragment.make_arms()
}
fn walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_arm(visitor, self)
}
}
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 walk_flat_map<V: MutVisitor>(self, visitor: &mut V) -> Self::OutputTy {
walk_flat_map_stmt(visitor, self)
}
fn is_mac_call(&self) -> bool {
match &self.kind {
StmtKind::MacCall(..) => true,
StmtKind::Item(item) => matches!(item.kind, ItemKind::MacCall(..)),
2022-01-07 14:54:16 +08:00
StmtKind::Semi(expr) => matches!(expr.kind, ExprKind::MacCall(..)),
StmtKind::Expr(..) => unreachable!(),
StmtKind::Let(..) | StmtKind::Empty => false,
}
}
2022-08-12 12:20:10 +10:00
fn take_mac_call(self) -> (P<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) => {
2022-01-07 14:54:16 +08:00
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)
}
_ => unreachable!(),
},
StmtKind::Semi(expr) => match expr.into_inner() {
2022-01-07 14:54:16 +08:00
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 })
}
2024-03-15 14:21:03 +03:00
fn delegation(&self) -> Option<(&ast::DelegationMac, &ast::Item<Self::ItemKind>)> {
match &self.kind {
StmtKind::Item(item) => match &item.kind {
ItemKind::DelegationMac(deleg) => Some((deleg, item)),
_ => None,
},
_ => None,
}
}
fn delegation_item_kind(deleg: Box<ast::Delegation>) -> Self::ItemKind {
ItemKind::Delegation(deleg)
}
fn from_item(item: ast::Item<Self::ItemKind>) -> Self {
ast::Stmt { id: ast::DUMMY_NODE_ID, span: item.span, kind: StmtKind::Item(P(item)) }
}
fn flatten_outputs(items: impl Iterator<Item = Self::OutputTy>) -> Self::OutputTy {
items.flatten().collect()
}
fn post_flat_map_node_collect_bang(stmts: &mut Self::OutputTy, add_semicolon: AddSemicolon) {
// If this is a macro invocation with a semicolon, then apply that
// semicolon to the final statement produced by expansion.
if matches!(add_semicolon, AddSemicolon::Yes) {
if let Some(stmt) = stmts.pop() {
stmts.push(stmt.add_trailing_semicolon());
}
}
}
}
impl InvocationCollectorNode for ast::Crate {
type OutputTy = ast::Crate;
const KIND: AstFragmentKind = AstFragmentKind::Crate;
fn to_annotatable(self) -> Annotatable {
Annotatable::Crate(self)
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
fragment.make_crate()
}
fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
walk_crate(visitor, self)
}
fn expand_cfg_false(
&mut self,
collector: &mut InvocationCollector<'_, '_>,
pos: usize,
_span: Span,
) {
// Attributes above `cfg(FALSE)` are left in place, because we may want to configure
// some global crate properties even on fully unconfigured crates.
self.attrs.truncate(pos);
// Standard prelude imports are left in the crate for backward compatibility.
self.items.truncate(collector.cx.num_standard_library_imports);
}
}
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 walk<V: MutVisitor>(&mut self, visitor: &mut V) {
walk_ty(visitor, self)
}
fn is_mac_call(&self) -> bool {
matches!(self.kind, ast::TyKind::MacCall(..))
}
2022-08-12 12:20:10 +10:00
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
let node = self.into_inner();
match node.kind {
TyKind::MacCall(mac) => (mac, AttrVec::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 walk<V: MutVisitor>(&mut self, visitor: &mut V) {
walk_pat(visitor, self)
}
fn is_mac_call(&self) -> bool {
2022-01-07 14:54:16 +08:00
matches!(self.kind, PatKind::MacCall(..))
}
2022-08-12 12:20:10 +10:00
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
let node = self.into_inner();
match node.kind {
PatKind::MacCall(mac) => (mac, AttrVec::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 descr() -> &'static str {
"an expression"
}
fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
walk_expr(visitor, self)
}
fn is_mac_call(&self) -> bool {
2022-01-07 14:54:16 +08:00
matches!(self.kind, ExprKind::MacCall(..))
}
2022-08-12 12:20:10 +10:00
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
let node = self.into_inner();
match node.kind {
2022-01-07 14:54:16 +08:00
ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No),
_ => unreachable!(),
}
}
}
struct OptExprTag;
impl InvocationCollectorNode for AstNodeWrapper<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 walk_flat_map<V: MutVisitor>(mut self, visitor: &mut V) -> Self::OutputTy {
walk_expr(visitor, &mut self.wrapped);
Some(self.wrapped)
}
fn is_mac_call(&self) -> bool {
matches!(self.wrapped.kind, ast::ExprKind::MacCall(..))
}
2022-08-12 12:20:10 +10:00
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
let node = self.wrapped.into_inner();
match node.kind {
2022-01-07 14:54:16 +08:00
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);
}
}
/// This struct is a hack to workaround unstable of `stmt_expr_attributes`.
/// It can be removed once that feature is stabilized.
struct MethodReceiverTag;
impl DummyAstNode for MethodReceiverTag {
fn dummy() -> MethodReceiverTag {
MethodReceiverTag
}
}
impl InvocationCollectorNode for AstNodeWrapper<P<ast::Expr>, MethodReceiverTag> {
type OutputTy = Self;
type AttrsTy = ast::AttrVec;
const KIND: AstFragmentKind = AstFragmentKind::MethodReceiverExpr;
fn descr() -> &'static str {
"an expression"
}
fn to_annotatable(self) -> Annotatable {
Annotatable::Expr(self.wrapped)
}
fn fragment_to_output(fragment: AstFragment) -> Self::OutputTy {
AstNodeWrapper::new(fragment.make_method_receiver_expr(), MethodReceiverTag)
}
fn walk<V: MutVisitor>(&mut self, visitor: &mut V) {
walk_expr(visitor, &mut self.wrapped)
}
fn is_mac_call(&self) -> bool {
matches!(self.wrapped.kind, ast::ExprKind::MacCall(..))
}
fn take_mac_call(self) -> (P<ast::MacCall>, Self::AttrsTy, AddSemicolon) {
let node = self.wrapped.into_inner();
match node.kind {
ExprKind::MacCall(mac) => (mac, node.attrs, AddSemicolon::No),
_ => unreachable!(),
}
}
}
2024-03-15 14:21:03 +03:00
fn build_single_delegations<'a, Node: InvocationCollectorNode>(
ecx: &ExtCtxt<'_>,
deleg: &'a ast::DelegationMac,
item: &'a ast::Item<Node::ItemKind>,
suffixes: &'a [(Ident, Option<Ident>)],
item_span: Span,
from_glob: bool,
) -> impl Iterator<Item = ast::Item<Node::ItemKind>> + 'a {
if suffixes.is_empty() {
// Report an error for now, to avoid keeping stem for resolution and
// stability checks.
let kind = String::from(if from_glob { "glob" } else { "list" });
ecx.dcx().emit_err(EmptyDelegationMac { span: item.span, kind });
}
suffixes.iter().map(move |&(ident, rename)| {
let mut path = deleg.prefix.clone();
path.segments.push(ast::PathSegment { ident, id: ast::DUMMY_NODE_ID, args: None });
ast::Item {
attrs: item.attrs.clone(),
id: ast::DUMMY_NODE_ID,
span: if from_glob { item_span } else { ident.span },
vis: item.vis.clone(),
kind: Node::delegation_item_kind(Box::new(ast::Delegation {
id: ast::DUMMY_NODE_ID,
qself: deleg.qself.clone(),
path,
Move `ast::Item::ident` into `ast::ItemKind`. `ast::Item` has an `ident` field. - It's always non-empty for these item kinds: `ExternCrate`, `Static`, `Const`, `Fn`, `Mod`, `TyAlias`, `Enum`, `Struct`, `Union`, `Trait`, `TraitAlias`, `MacroDef`, `Delegation`. - It's always empty for these item kinds: `Use`, `ForeignMod`, `GlobalAsm`, `Impl`, `MacCall`, `DelegationMac`. There is a similar story for `AssocItemKind` and `ForeignItemKind`. Some sites that handle items check for an empty ident, some don't. This is a very C-like way of doing things, but this is Rust, we have sum types, we can do this properly and never forget to check for the exceptional case and never YOLO possibly empty identifiers (or possibly dummy spans) around and hope that things will work out. The commit is large but it's mostly obvious plumbing work. Some notable things. - `ast::Item` got 8 bytes bigger. This could be avoided by boxing the fields within some of the `ast::ItemKind` variants (specifically: `Struct`, `Union`, `Enum`). I might do that in a follow-up; this commit is big enough already. - For the visitors: `FnKind` no longer needs an `ident` field because the `Fn` within how has one. - In the parser, the `ItemInfo` typedef is no longer needed. It was used in various places to return an `Ident` alongside an `ItemKind`, but now the `Ident` (if present) is within the `ItemKind`. - In a few places I renamed identifier variables called `name` (or `foo_name`) as `ident` (or `foo_ident`), to better match the type, and because `name` is normally used for `Symbol`s. It's confusing to see something like `foo_name.name`.
2025-03-21 09:47:43 +11:00
ident: rename.unwrap_or(ident),
2024-03-15 14:21:03 +03:00
rename,
body: deleg.body.clone(),
from_glob,
})),
tokens: None,
}
})
}
struct InvocationCollector<'a, 'b> {
2016-09-02 09:12:47 +00:00
cx: &'a mut ExtCtxt<'b>,
invocations: Vec<(Invocation, Option<Arc<SyntaxExtension>>)>,
monotonic: bool,
2016-09-02 09:12:47 +00:00
}
impl<'a, 'b> InvocationCollector<'a, 'b> {
fn cfg(&self) -> StripUnconfigured<'_> {
StripUnconfigured {
sess: self.cx.sess,
features: Some(self.cx.ecfg.features),
config_tokens: false,
lint_node_id: self.cx.current_expansion.lint_node_id,
}
}
fn collect(&mut self, fragment_kind: AstFragmentKind, kind: InvocationKind) -> AstFragment {
2021-06-25 20:43:04 +02:00
let expn_id = LocalExpnId::fresh_empty();
2024-03-15 14:21:03 +03:00
if matches!(kind, InvocationKind::GlobDelegation { .. }) {
// In resolver we need to know which invocation ids are delegations early,
// before their `ExpnData` is filled.
self.cx.resolver.register_glob_delegation(expn_id);
}
let vis = kind.placeholder_visibility();
self.invocations.push((
Invocation {
kind,
fragment_kind,
expansion_data: ExpansionData {
id: expn_id,
depth: self.cx.current_expansion.depth + 1,
..self.cx.current_expansion.clone()
},
2016-09-26 22:54:36 +00:00
},
None,
));
placeholder(fragment_kind, NodeId::placeholder_from_expn_id(expn_id), vis)
2016-09-02 09:12:47 +00:00
}
2022-08-12 12:20:10 +10:00
fn collect_bang(&mut self, mac: P<ast::MacCall>, kind: AstFragmentKind) -> AstFragment {
// cache the macro call span so that it can be
// easily adjusted for incremental compilation
let span = mac.span();
self.collect(kind, InvocationKind::Bang { mac, span })
2016-09-02 09:12:47 +00:00
}
fn collect_attr(
&mut self,
2022-01-07 14:54:16 +08:00
(attr, pos, derives): (ast::Attribute, usize, Vec<ast::Path>),
item: Annotatable,
kind: AstFragmentKind,
) -> AstFragment {
self.collect(kind, InvocationKind::Attr { attr, pos, item, derives })
2016-09-02 09:12:47 +00:00
}
2024-03-15 14:21:03 +03:00
fn collect_glob_delegation(
&mut self,
item: P<ast::AssocItem>,
of_trait: bool,
2024-03-15 14:21:03 +03:00
kind: AstFragmentKind,
) -> AstFragment {
self.collect(kind, InvocationKind::GlobDelegation { item, of_trait })
2024-03-15 14:21:03 +03:00
}
/// If `item` is an attribute invocation, remove the attribute and return it together with
/// its position and derives following it. We have to collect the derives in order to resolve
/// legacy derive helpers (helpers written before derives that introduce them).
fn take_first_attr(
&self,
item: &mut impl HasAttrs,
2022-01-07 14:54:16 +08:00
) -> 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.is_some_and(rustc_feature::is_builtin_attr_name)
{
attr_pos = Some(pos); // a non-cfg attr found, still may find a cfg attr
}
}
}
item.visit_attrs(|attrs| {
attr = Some(match (cfg_pos, attr_pos) {
(Some(pos), _) => (attrs.remove(pos), pos, Vec::new()),
(_, Some(pos)) => {
let attr = attrs.remove(pos);
let following_derives = attrs[pos..]
.iter()
.filter(|a| a.has_name(sym::derive))
.flat_map(|a| a.meta_item_list().unwrap_or_default())
2024-10-07 08:49:47 +09:00
.filter_map(|meta_item_inner| match meta_item_inner {
2024-10-04 21:59:04 +09:00
MetaItemInner::MetaItem(ast::MetaItem {
kind: MetaItemKind::Word,
path,
..
}) => Some(path),
_ => None,
})
.collect();
(attr, pos, following_derives)
}
_ => return,
});
});
attr
}
// Detect use of feature-gated or invalid attributes on macro invocations
// since they will not be detected after macro expansion.
2022-01-07 14:54:16 +08:00
fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) {
let features = self.cx.ecfg.features;
let mut attrs = attrs.iter().peekable();
let mut span: Option<Span> = None;
while let Some(attr) = attrs.next() {
rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features);
2024-08-07 01:36:28 -05:00
validate_attr::check_attr(&self.cx.sess.psess, attr);
let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span };
span = Some(current_span);
if attrs.peek().is_some_and(|next_attr| next_attr.doc_str().is_some()) {
continue;
}
if attr.is_doc_comment() {
self.cx.sess.psess.buffer_lint(
UNUSED_DOC_COMMENTS,
current_span,
self.cx.current_expansion.lint_node_id,
BuiltinLintDiag::UnusedDocComment(attr.span),
2020-02-21 16:46:14 -08:00
);
} else if rustc_attr_parsing::is_builtin_attr(attr) {
let attr_name = attr.ident().unwrap().name;
// `#[cfg]` and `#[cfg_attr]` are special - they are
// eagerly evaluated.
if attr_name != sym::cfg_trace && attr_name != sym::cfg_attr_trace {
self.cx.sess.psess.buffer_lint(
UNUSED_ATTRIBUTES,
attr.span,
self.cx.current_expansion.lint_node_id,
BuiltinLintDiag::UnusedBuiltinAttribute {
attr_name,
macro_name: pprust::path_to_string(&call.path),
invoc_span: call.path.span,
},
);
}
}
}
}
fn expand_cfg_true(
&mut self,
node: &mut impl HasAttrs,
attr: ast::Attribute,
pos: usize,
) -> (bool, Option<ast::MetaItem>) {
let (res, meta_item) = self.cfg().cfg_true(&attr);
if res {
// A trace attribute left in AST in place of the original `cfg` attribute.
// It can later be used by lints or other diagnostics.
let trace_attr = attr_into_trace(attr, sym::cfg_trace);
node.visit_attrs(|attrs| attrs.insert(pos, trace_attr));
}
(res, meta_item)
}
fn expand_cfg_attr(&self, node: &mut impl HasAttrs, attr: &ast::Attribute, pos: usize) {
node.visit_attrs(|attrs| {
// Repeated `insert` calls is inefficient, but the number of
// insertions is almost always 0 or 1 in practice.
for cfg in self.cfg().expand_cfg_attr(attr, false).into_iter().rev() {
attrs.insert(pos, cfg)
}
});
}
fn flat_map_node<Node: InvocationCollectorNode<OutputTy: Default>>(
&mut self,
mut node: Node,
) -> Node::OutputTy {
loop {
return match self.take_first_attr(&mut node) {
Some((attr, pos, derives)) => match attr.name_or_empty() {
sym::cfg => {
let (res, meta_item) = self.expand_cfg_true(&mut node, attr, pos);
if res {
continue;
}
if let Some(meta_item) = meta_item {
for name in node.declared_names() {
self.cx.resolver.append_stripped_cfg_item(
self.cx.current_expansion.lint_node_id,
name,
meta_item.clone(),
)
}
}
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
}
2024-03-15 14:21:03 +03:00
None if let Some((deleg, item)) = node.delegation() => {
let Some(suffixes) = &deleg.suffixes else {
let traitless_qself =
matches!(&deleg.qself, Some(qself) if qself.position == 0);
let (item, of_trait) = match node.to_annotatable() {
Annotatable::AssocItem(item, AssocCtxt::Impl { of_trait }) => {
(item, of_trait)
}
2024-03-15 14:21:03 +03:00
ann @ (Annotatable::Item(_)
| Annotatable::AssocItem(..)
2024-03-15 14:21:03 +03:00
| Annotatable::Stmt(_)) => {
let span = ann.span();
self.cx.dcx().emit_err(GlobDelegationOutsideImpls { span });
return Default::default();
}
_ => unreachable!(),
};
if traitless_qself {
let span = item.span;
self.cx.dcx().emit_err(GlobDelegationTraitlessQpath { span });
return Default::default();
}
return self
.collect_glob_delegation(item, of_trait, Node::KIND)
.make_ast::<Node>();
2024-03-15 14:21:03 +03:00
};
2024-03-15 14:21:03 +03:00
let single_delegations = build_single_delegations::<Node>(
self.cx, deleg, item, suffixes, item.span, false,
);
Node::flatten_outputs(single_delegations.map(|item| {
let mut item = Node::from_item(item);
assign_id!(self, item.node_id_mut(), || item.walk_flat_map(self))
}))
}
None => {
match Node::wrap_flat_map_node_walk_flat_map(node, self, |mut node, this| {
assign_id!(this, node.node_id_mut(), || node.walk_flat_map(this))
}) {
Ok(output) => output,
Err(returned_node) => {
node = returned_node;
continue;
}
}
}
};
}
}
fn visit_node<Node: InvocationCollectorNode<OutputTy = 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).0 {
continue;
}
node.expand_cfg_false(self, pos, span);
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>()
})
}
2024-03-15 14:21:03 +03:00
None if node.delegation().is_some() => unreachable!(),
None => {
assign_id!(self, node.node_id_mut(), || node.walk(self))
}
};
}
}
}
Overhaul `syntax::fold::Folder`. This commit changes `syntax::fold::Folder` from a functional style (where most methods take a `T` and produce a new `T`) to a more imperative style (where most methods take and modify a `&mut T`), and renames it `syntax::mut_visit::MutVisitor`. The first benefit is speed. The functional style does not require any reallocations, due to the use of `P::map` and `MoveMap::move_{,flat_}map`. However, every field in the AST must be overwritten; even those fields that are unchanged are overwritten with the same value. This causes a lot of unnecessary memory writes. The imperative style reduces instruction counts by 1--3% across a wide range of workloads, particularly incremental workloads. The second benefit is conciseness; the imperative style is usually more concise. E.g. compare the old functional style: ``` fn fold_abc(&mut self, abc: ABC) { ABC { a: fold_a(abc.a), b: fold_b(abc.b), c: abc.c, } } ``` with the imperative style: ``` fn visit_abc(&mut self, ABC { a, b, c: _ }: &mut ABC) { visit_a(a); visit_b(b); } ``` (The reductions get larger in more complex examples.) Overall, the patch removes over 200 lines of code -- even though the new code has more comments -- and a lot of the remaining lines have fewer characters. Some notes: - The old style used methods called `fold_*`. The new style mostly uses methods called `visit_*`, but there are a few methods that map a `T` to something other than a `T`, which are called `flat_map_*` (`T` maps to multiple `T`s) or `filter_map_*` (`T` maps to 0 or 1 `T`s). - `move_map.rs`/`MoveMap`/`move_map`/`move_flat_map` are renamed `map_in_place.rs`/`MapInPlace`/`map_in_place`/`flat_map_in_place` to reflect their slightly changed signatures. - Although this commit renames the `fold` module as `mut_visit`, it keeps it in the `fold.rs` file, so as not to confuse git. The next commit will rename the file.
2019-02-05 15:20:55 +11:00
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_assoc_item(
&mut self,
node: P<ast::AssocItem>,
ctxt: AssocCtxt,
) -> SmallVec<[P<ast::AssocItem>; 1]> {
match ctxt {
AssocCtxt::Trait => self.flat_map_node(AstNodeWrapper::new(node, TraitItemTag)),
AssocCtxt::Impl { of_trait: false } => {
self.flat_map_node(AstNodeWrapper::new(node, ImplItemTag))
}
AssocCtxt::Impl { of_trait: true } => {
self.flat_map_node(AstNodeWrapper::new(node, TraitImplItemTag))
}
}
}
fn flat_map_foreign_item(
&mut self,
node: P<ast::ForeignItem>,
) -> SmallVec<[P<ast::ForeignItem>; 1]> {
self.flat_map_node(node)
}
fn flat_map_variant(&mut self, node: ast::Variant) -> SmallVec<[ast::Variant; 1]> {
self.flat_map_node(node)
}
fn flat_map_where_predicate(
&mut self,
node: ast::WherePredicate,
) -> SmallVec<[ast::WherePredicate; 1]> {
self.flat_map_node(node)
}
fn flat_map_field_def(&mut self, node: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> {
self.flat_map_node(node)
}
fn flat_map_pat_field(&mut self, node: ast::PatField) -> SmallVec<[ast::PatField; 1]> {
self.flat_map_node(node)
}
fn flat_map_expr_field(&mut self, node: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> {
self.flat_map_node(node)
}
fn flat_map_param(&mut self, node: ast::Param) -> SmallVec<[ast::Param; 1]> {
self.flat_map_node(node)
}
fn flat_map_generic_param(
&mut self,
node: ast::GenericParam,
) -> SmallVec<[ast::GenericParam; 1]> {
self.flat_map_node(node)
}
fn flat_map_arm(&mut self, node: ast::Arm) -> SmallVec<[ast::Arm; 1]> {
self.flat_map_node(node)
}
fn flat_map_stmt(&mut self, node: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
// FIXME: invocations in semicolon-less expressions positions are expanded as expressions,
// changing that requires some compatibility measures.
if node.is_expr() {
// The only way that we can end up with a `MacCall` expression statement,
// (as opposed to a `StmtKind::MacCall`) is if we have a macro as the
// trailing expression in a block (e.g. `fn foo() { my_macro!() }`).
// Record this information, so that we can report a more specific
// `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint if needed.
// See #78991 for an investigation of treating macros in this position
// as statements, rather than expressions, during parsing.
return match &node.kind {
StmtKind::Expr(expr)
2022-01-07 14:54:16 +08:00
if matches!(**expr, ast::Expr { kind: ExprKind::MacCall(..), .. }) =>
{
self.cx.current_expansion.is_trailing_mac = true;
// Don't use `assign_id` for this statement - it may get removed
// entirely due to a `#[cfg]` on the contained expression
let res = walk_flat_map_stmt(self, node);
self.cx.current_expansion.is_trailing_mac = false;
res
}
_ => walk_flat_map_stmt(self, node),
};
}
self.flat_map_node(node)
}
fn visit_crate(&mut self, node: &mut ast::Crate) {
self.visit_node(node)
}
fn visit_ty(&mut self, node: &mut P<ast::Ty>) {
self.visit_node(node)
2014-07-04 11:24:28 -07:00
}
fn visit_pat(&mut self, node: &mut P<ast::Pat>) {
self.visit_node(node)
}
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 visit_method_receiver_expr(&mut self, node: &mut P<ast::Expr>) {
visit_clobber(node, |node| {
let mut wrapper = AstNodeWrapper::new(node, MethodReceiverTag);
self.visit_node(&mut wrapper);
wrapper.wrapped
})
}
fn filter_map_expr(&mut self, node: P<ast::Expr>) -> Option<P<ast::Expr>> {
self.flat_map_node(AstNodeWrapper::new(node, OptExprTag))
}
2022-01-07 14:54:16 +08:00
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,
);
walk_block(self, node);
self.cx.current_expansion.dir_ownership = orig_dir_ownership;
}
2022-01-07 14:54:16 +08:00
fn visit_id(&mut self, id: &mut NodeId) {
// We may have already assigned a `NodeId`
// by calling `assign_id`
if self.monotonic && *id == ast::DUMMY_NODE_ID {
*id = self.cx.resolver.next_node_id();
}
}
}
pub struct ExpansionConfig<'feat> {
pub crate_name: String,
pub features: &'feat Features,
pub recursion_limit: Limit,
pub trace_mac: bool,
/// If false, strip `#[test]` nodes
pub should_test: bool,
/// If true, use verbose debugging for `proc_macro::Span`
pub span_debug: bool,
/// If true, show backtraces for proc-macro panics
pub proc_macro_backtrace: bool,
}
impl ExpansionConfig<'_> {
pub fn default(crate_name: String, features: &Features) -> ExpansionConfig<'_> {
ExpansionConfig {
crate_name,
features,
recursion_limit: Limit::new(1024),
trace_mac: false,
should_test: false,
span_debug: false,
proc_macro_backtrace: false,
}
}
}