Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expand procedural attribute macros #9128

Merged
merged 4 commits into from
Jun 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,18 @@ impl Module {
Some(derive_name.clone()),
)
}
MacroCallKind::Attr { ast_id, invoc_attr_index, attr_name, .. } => {
let node = ast_id.to_node(db.upcast());
let attr =
node.attrs().nth((*invoc_attr_index) as usize).unwrap_or_else(
|| panic!("cannot find attribute #{}", invoc_attr_index),
);
(
ast_id.file_id,
SyntaxNodePtr::from(AstPtr::new(&attr)),
Some(attr_name.clone()),
)
}
};
sink.push(UnresolvedProcMacro {
file,
Expand All @@ -558,7 +570,9 @@ impl Module {
let node = ast_id.to_node(db.upcast());
(ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
}
MacroCallKind::Derive { ast_id, .. } => {
MacroCallKind::Derive { ast_id, .. }
| MacroCallKind::Attr { ast_id, .. } => {
// FIXME: point to the attribute instead, this creates very large diagnostics
let node = ast_id.to_node(db.upcast());
(ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)))
}
Expand Down
38 changes: 22 additions & 16 deletions crates/hir_def/src/builtin_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`.
//!
//! It was last synchronized with upstream commit 2225ee1b62ff089917434aefd9b2bf509cfa087f.
//! It was last synchronized with upstream commit 835150e70288535bc57bb624792229b9dc94991d.
//!
//! The macros were adjusted to only expand to the attribute name, since that is all we need to do
//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
Expand Down Expand Up @@ -58,7 +58,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
ungated!(reexport_test_harness_main, Normal, template!(NameValueStr: "name")),

// Macros:
ungated!(derive, Normal, template!(List: "Trait1, Trait2, ...")),
ungated!(automatically_derived, Normal, template!(Word)),
// FIXME(#14407)
ungated!(macro_use, Normal, template!(Word, List: "name1, name2, ...")),
Expand Down Expand Up @@ -98,8 +97,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#),
),
ungated!(link_name, AssumedUsed, template!(NameValueStr: "name")),
ungated!(no_link, Normal, template!(Word)),
ungated!(repr, Normal, template!(List: "C")),
ungated!(no_link, AssumedUsed, template!(Word)),
ungated!(repr, AssumedUsed, template!(List: "C")),
ungated!(export_name, AssumedUsed, template!(NameValueStr: "name")),
ungated!(link_section, AssumedUsed, template!(NameValueStr: "name")),
ungated!(no_mangle, AssumedUsed, template!(Word)),
Expand All @@ -112,6 +111,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
const_eval_limit, CrateLevel, template!(NameValueStr: "N"), const_eval_limit,
experimental!(const_eval_limit)
),
gated!(
move_size_limit, CrateLevel, template!(NameValueStr: "N"), large_assignments,
experimental!(move_size_limit)
),

// Entry point:
ungated!(main, Normal, template!(Word)),
Expand Down Expand Up @@ -140,6 +143,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
template!(List: "address, memory, thread"),
experimental!(no_sanitize)
),
gated!(no_coverage, AssumedUsed, template!(Word), experimental!(no_coverage)),

// FIXME: #14408 assume docs are used since rustdoc looks at them.
ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")),
Expand All @@ -150,11 +154,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[

// Linking:
gated!(naked, AssumedUsed, template!(Word), naked_functions, experimental!(naked)),
gated!(
link_args, Normal, template!(NameValueStr: "args"),
"the `link_args` attribute is experimental and not portable across platforms, \
it is recommended to use `#[link(name = \"foo\")] instead",
),
gated!(
link_ordinal, AssumedUsed, template!(List: "ordinal"), raw_dylib,
experimental!(link_ordinal)
Expand All @@ -172,7 +171,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
"custom test frameworks are an unstable feature",
),
// RFC #1268
gated!(marker, Normal, template!(Word), marker_trait_attr, experimental!(marker)),
gated!(marker, AssumedUsed, template!(Word), marker_trait_attr, experimental!(marker)),
gated!(
thread_local, AssumedUsed, template!(Word),
"`#[thread_local]` is an experimental feature, and does not currently handle destructors",
Expand Down Expand Up @@ -291,7 +290,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// Internal attributes, Macro related:
// ==========================================================================

rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word), IMPL_DETAIL),
rustc_attr!(rustc_builtin_macro, AssumedUsed, template!(Word, NameValueStr: "name"), IMPL_DETAIL),
rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE),
rustc_attr!(
rustc_macro_transparency, AssumedUsed,
Expand Down Expand Up @@ -319,7 +318,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// ==========================================================================

rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL),
rustc_attr!(rustc_args_required_const, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
rustc_attr!(rustc_legacy_const_generics, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),

// ==========================================================================
// Internal attributes, Layout related:
Expand Down Expand Up @@ -380,13 +379,23 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_specialization_trait, Normal, template!(Word),
"the `#[rustc_specialization_trait]` attribute is used to check specializations"
),
rustc_attr!(
rustc_main, Normal, template!(Word),
"the `#[rustc_main]` attribute is used internally to specify test entry point function",
),
rustc_attr!(
rustc_skip_array_during_method_dispatch, Normal, template!(Word),
"the `#[rustc_skip_array_during_method_dispatch]` attribute is used to exclude a trait \
from method dispatch when the receiver is an array, for compatibility in editions < 2021."
),

// ==========================================================================
// Internal attributes, Testing:
// ==========================================================================

rustc_attr!(TEST, rustc_outlives, Normal, template!(Word)),
rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word)),
rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word)),
rustc_attr!(TEST, rustc_variance, Normal, template!(Word)),
rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")),
rustc_attr!(TEST, rustc_regions, Normal, template!(Word)),
Expand All @@ -395,12 +404,9 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
template!(Word, List: "delay_span_bug_from_inside_query")
),
rustc_attr!(TEST, rustc_dump_user_substs, AssumedUsed, template!(Word)),
rustc_attr!(TEST, rustc_evaluate_where_clauses, AssumedUsed, template!(Word)),
rustc_attr!(TEST, rustc_if_this_changed, AssumedUsed, template!(Word, List: "DepNode")),
rustc_attr!(TEST, rustc_then_this_would_need, AssumedUsed, template!(List: "DepNode")),
rustc_attr!(
TEST, rustc_dirty, AssumedUsed,
template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#),
),
rustc_attr!(
TEST, rustc_clean, AssumedUsed,
template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#),
Expand Down
3 changes: 3 additions & 0 deletions crates/hir_def/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ pub trait InternDatabase: SourceDatabase {

#[salsa::query_group(DefDatabaseStorage)]
pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
#[salsa::input]
fn enable_proc_attr_macros(&self) -> bool;

#[salsa::invoke(ItemTree::file_item_tree_query)]
fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;

Expand Down
40 changes: 40 additions & 0 deletions crates/hir_def/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ use std::{
sync::Arc,
};

use attr::Attr;
use base_db::{impl_intern_key, salsa, CrateId};
use hir_expand::{
ast_id_map::FileAstId,
Expand Down Expand Up @@ -768,3 +769,42 @@ fn derive_macro_as_call_id(
.into();
Ok(res)
}

fn attr_macro_as_call_id(
item_attr: &AstIdWithPath<ast::Item>,
macro_attr: &Attr,
db: &dyn db::DefDatabase,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
) -> Result<MacroCallId, UnresolvedMacro> {
let def: MacroDefId = resolver(item_attr.path.clone())
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
let last_segment = item_attr
.path
.segments()
.last()
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
let mut arg = match &macro_attr.input {
Some(input) => match &**input {
attr::AttrInput::Literal(_) => tt::Subtree::default(),
attr::AttrInput::TokenTree(tt) => tt.clone(),
},
None => tt::Subtree::default(),
};
// The parentheses are always disposed here.
arg.delimiter = None;

let res = def
.as_lazy_macro(
db.upcast(),
krate,
MacroCallKind::Attr {
ast_id: item_attr.ast_id,
attr_name: last_segment.to_string(),
attr_args: arg,
invoc_attr_index: macro_attr.id.ast_index,
},
)
.into();
Ok(res)
}
60 changes: 54 additions & 6 deletions crates/hir_def/src/nameres/collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use syntax::ast;

use crate::{
attr::{Attr, AttrId, AttrInput, Attrs},
builtin_attr,
attr_macro_as_call_id, builtin_attr,
db::DefDatabase,
derive_macro_as_call_id,
intern::Interned,
Expand Down Expand Up @@ -223,7 +223,7 @@ struct MacroDirective {
enum MacroDirectiveKind {
FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind },
Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId },
Attr { ast_id: AstIdWithPath<ast::Item>, attr: AttrId, mod_item: ModItem },
Attr { ast_id: AstIdWithPath<ast::Item>, attr: Attr, mod_item: ModItem },
}

struct DefData<'a> {
Expand Down Expand Up @@ -419,7 +419,7 @@ impl DefCollector<'_> {
let mut unresolved_macros = std::mem::replace(&mut self.unresolved_macros, Vec::new());
let pos = unresolved_macros.iter().position(|directive| {
if let MacroDirectiveKind::Attr { ast_id, mod_item, attr } = &directive.kind {
self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr);
self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), attr.id);

let file_id = ast_id.ast_id.file_id;
let item_tree = self.db.file_item_tree(file_id);
Expand Down Expand Up @@ -1050,7 +1050,7 @@ impl DefCollector<'_> {
let file_id = ast_id.ast_id.file_id;
let item_tree = self.db.file_item_tree(file_id);
let mod_dir = self.mod_dirs[&directive.module_id].clone();
self.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr);
self.skip_attrs.insert(InFile::new(file_id, *mod_item), attr.id);
ModCollector {
def_collector: &mut *self,
macro_depth: directive.depth,
Expand All @@ -1067,8 +1067,56 @@ impl DefCollector<'_> {
}
}

if !self.db.enable_proc_attr_macros() {
return true;
}

// Not resolved to a derive helper, so try to resolve as a macro.
// FIXME: not yet :)
match attr_macro_as_call_id(
ast_id,
attr,
self.db,
self.def_map.krate,
&resolver,
) {
Ok(call_id) => {
let loc: MacroCallLoc = self.db.lookup_intern_macro(call_id);
if let MacroDefKind::ProcMacro(exp, ..) = &loc.def.kind {
if exp.is_dummy() {
// Proc macros that cannot be expanded are treated as not
// resolved, in order to fall back later.
self.def_map.diagnostics.push(
DefDiagnostic::unresolved_proc_macro(
directive.module_id,
loc.kind,
),
);

let file_id = ast_id.ast_id.file_id;
let item_tree = self.db.file_item_tree(file_id);
let mod_dir = self.mod_dirs[&directive.module_id].clone();
self.skip_attrs
.insert(InFile::new(file_id, *mod_item), attr.id);
ModCollector {
def_collector: &mut *self,
macro_depth: directive.depth,
module_id: directive.module_id,
file_id,
item_tree: &item_tree,
mod_dir,
}
.collect(&[*mod_item]);

// Remove the macro directive.
return false;
}
}
resolved.push((directive.module_id, call_id, directive.depth));
res = ReachedFixedPoint::No;
return false;
}
Err(UnresolvedMacro { .. }) => (),
}
}
}

Expand Down Expand Up @@ -1628,7 +1676,7 @@ impl ModCollector<'_, '_> {
self.def_collector.unresolved_macros.push(MacroDirective {
module_id: self.module_id,
depth: self.macro_depth + 1,
kind: MacroDirectiveKind::Attr { ast_id, attr: attr.id, mod_item },
kind: MacroDirectiveKind::Attr { ast_id, attr: attr.clone(), mod_item },
});

return Err(());
Expand Down
9 changes: 8 additions & 1 deletion crates/hir_def/src/test_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,19 @@ use crate::{
crate::db::InternDatabaseStorage,
crate::db::DefDatabaseStorage
)]
#[derive(Default)]
pub(crate) struct TestDB {
storage: salsa::Storage<TestDB>,
events: Mutex<Option<Vec<salsa::Event>>>,
}

impl Default for TestDB {
fn default() -> Self {
let mut this = Self { storage: Default::default(), events: Default::default() };
this.set_enable_proc_attr_macros(true);
this
}
}

impl Upcast<dyn AstDatabase> for TestDB {
fn upcast(&self) -> &(dyn AstDatabase + 'static) {
&*self
Expand Down
11 changes: 8 additions & 3 deletions crates/hir_expand/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use syntax::{

use crate::{
ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander,
BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallLoc, MacroDefId,
MacroDefKind, MacroFile, ProcMacroExpander,
BuiltinFnLikeExpander, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc,
MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
};

/// Total limit on the number of tokens produced by any macro invocation.
Expand Down Expand Up @@ -377,7 +377,12 @@ fn expand_proc_macro(
_ => unreachable!(),
};

expander.expand(db, loc.krate, &macro_arg.0)
let attr_arg = match &loc.kind {
MacroCallKind::Attr { attr_args, .. } => Some(attr_args),
_ => None,
};

expander.expand(db, loc.krate, &macro_arg.0, attr_arg)
}

fn is_self_replicating(from: &SyntaxNode, to: &SyntaxNode) -> bool {
Expand Down
19 changes: 19 additions & 0 deletions crates/hir_expand/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ pub(crate) fn process_macro_input(

remove_derives_up_to(item, derive_attr_index as usize).syntax().clone()
}
MacroCallKind::Attr { invoc_attr_index, .. } => {
let item = match ast::Item::cast(node.clone()) {
Some(item) => item,
None => return node,
};

remove_attr_invoc(item, invoc_attr_index as usize).syntax().clone()
}
}
}

Expand All @@ -46,6 +54,17 @@ fn remove_derives_up_to(item: ast::Item, attr_index: usize) -> ast::Item {
item
}

/// Removes the attribute invoking an attribute macro from `item`.
fn remove_attr_invoc(item: ast::Item, attr_index: usize) -> ast::Item {
let item = item.clone_for_update();
let attr = item
.attrs()
.nth(attr_index)
.unwrap_or_else(|| panic!("cannot find attribute #{}", attr_index));
attr.syntax().detach();
item
}

#[cfg(test)]
mod tests {
use base_db::fixture::WithFixture;
Expand Down
Loading