mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
Expand attribute macros on impl and trait items
This commit is contained in:
parent
3eddda6f4c
commit
3ff68f25b8
8 changed files with 200 additions and 73 deletions
|
@ -12,7 +12,8 @@ use cfg::{CfgExpr, CfgOptions};
|
|||
use drop_bomb::DropBomb;
|
||||
use either::Either;
|
||||
use hir_expand::{
|
||||
ast_id_map::AstIdMap, hygiene::Hygiene, AstId, ExpandResult, HirFileId, InFile, MacroDefId,
|
||||
ast_id_map::AstIdMap, hygiene::Hygiene, AstId, ExpandError, ExpandResult, HirFileId, InFile,
|
||||
MacroCallId, MacroDefId,
|
||||
};
|
||||
use la_arena::{Arena, ArenaMap};
|
||||
use limit::Limit;
|
||||
|
@ -124,6 +125,23 @@ impl Expander {
|
|||
}
|
||||
};
|
||||
|
||||
Ok(self.enter_expand_inner(db, call_id, err))
|
||||
}
|
||||
|
||||
pub fn enter_expand_id<T: ast::AstNode>(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
call_id: MacroCallId,
|
||||
) -> ExpandResult<Option<(Mark, T)>> {
|
||||
self.enter_expand_inner(db, call_id, None)
|
||||
}
|
||||
|
||||
fn enter_expand_inner<T: ast::AstNode>(
|
||||
&mut self,
|
||||
db: &dyn DefDatabase,
|
||||
call_id: MacroCallId,
|
||||
mut err: Option<ExpandError>,
|
||||
) -> ExpandResult<Option<(Mark, T)>> {
|
||||
if err.is_none() {
|
||||
err = db.macro_expand_error(call_id);
|
||||
}
|
||||
|
@ -138,9 +156,9 @@ impl Expander {
|
|||
tracing::warn!("no error despite `parse_or_expand` failing");
|
||||
}
|
||||
|
||||
return Ok(ExpandResult::only_err(err.unwrap_or_else(|| {
|
||||
return ExpandResult::only_err(err.unwrap_or_else(|| {
|
||||
mbe::ExpandError::Other("failed to parse macro invocation".into())
|
||||
})));
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -148,7 +166,7 @@ impl Expander {
|
|||
Some(it) => it,
|
||||
None => {
|
||||
// This can happen without being an error, so only forward previous errors.
|
||||
return Ok(ExpandResult { value: None, err });
|
||||
return ExpandResult { value: None, err };
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -164,7 +182,7 @@ impl Expander {
|
|||
self.current_file_id = file_id;
|
||||
self.ast_id_map = db.ast_id_map(file_id);
|
||||
|
||||
Ok(ExpandResult { value: Some((mark, node)), err })
|
||||
ExpandResult { value: Some((mark, node)), err }
|
||||
}
|
||||
|
||||
pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
|
||||
|
|
|
@ -30,6 +30,7 @@ pub trait ChildBySource {
|
|||
impl ChildBySource for TraitId {
|
||||
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
|
||||
let data = db.trait_data(*self);
|
||||
// FIXME attribute macros
|
||||
for (_name, item) in data.items.iter() {
|
||||
match *item {
|
||||
AssocItemId::FunctionId(func) => {
|
||||
|
@ -61,6 +62,7 @@ impl ChildBySource for TraitId {
|
|||
impl ChildBySource for ImplId {
|
||||
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
|
||||
let data = db.impl_data(*self);
|
||||
// FIXME attribute macros
|
||||
for &item in data.items.iter() {
|
||||
match item {
|
||||
AssocItemId::FunctionId(func) => {
|
||||
|
|
|
@ -2,19 +2,20 @@
|
|||
|
||||
use std::sync::Arc;
|
||||
|
||||
use hir_expand::{name::Name, InFile};
|
||||
use hir_expand::{name::Name, AstId, ExpandResult, InFile};
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
attr::Attrs,
|
||||
body::Expander,
|
||||
body::{Expander, Mark},
|
||||
db::DefDatabase,
|
||||
intern::Interned,
|
||||
item_tree::{self, AssocItem, FnFlags, ItemTreeId, ModItem, Param},
|
||||
nameres::attr_resolution::ResolvedAttr,
|
||||
type_ref::{TraitRef, TypeBound, TypeRef},
|
||||
visibility::RawVisibility,
|
||||
AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, Intern,
|
||||
ItemContainerId, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||
AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
|
||||
Intern, ItemContainerId, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -348,14 +349,29 @@ fn collect_items(
|
|||
let item_tree = tree_id.item_tree(db);
|
||||
let crate_graph = db.crate_graph();
|
||||
let cfg_options = &crate_graph[module.krate].cfg_options;
|
||||
let def_map = module.def_map(db);
|
||||
|
||||
let mut items = Vec::new();
|
||||
for item in assoc_items {
|
||||
'items: for item in assoc_items {
|
||||
let attrs = item_tree.attrs(db, module.krate, ModItem::from(item).into());
|
||||
if !attrs.is_cfg_enabled(cfg_options) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for attr in &*attrs {
|
||||
let ast_id = AstIdWithPath {
|
||||
path: (*attr.path).clone(),
|
||||
ast_id: AstId::new(expander.current_file_id(), item.ast_id(&item_tree).upcast()),
|
||||
};
|
||||
if let Ok(ResolvedAttr::Macro(call_id)) =
|
||||
def_map.resolve_attr_macro(db, module.local_id, ast_id, attr)
|
||||
{
|
||||
let res = expander.enter_expand_id(db, call_id);
|
||||
items.extend(collect_macro_items(db, module, expander, container, limit, res));
|
||||
continue 'items;
|
||||
}
|
||||
}
|
||||
|
||||
match item {
|
||||
AssocItem::Function(id) => {
|
||||
let item = &item_tree[id];
|
||||
|
@ -385,24 +401,7 @@ fn collect_items(
|
|||
let res = expander.enter_expand(db, call);
|
||||
|
||||
if let Ok(res) = res {
|
||||
if let Some((mark, mac)) = res.value {
|
||||
let src: InFile<ast::MacroItems> = expander.to_source(mac);
|
||||
let tree_id = item_tree::TreeId::new(src.file_id, None);
|
||||
let item_tree = tree_id.item_tree(db);
|
||||
let iter =
|
||||
item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
|
||||
items.extend(collect_items(
|
||||
db,
|
||||
module,
|
||||
expander,
|
||||
iter,
|
||||
tree_id,
|
||||
container,
|
||||
limit - 1,
|
||||
));
|
||||
|
||||
expander.exit(db, mark);
|
||||
}
|
||||
items.extend(collect_macro_items(db, module, expander, container, limit, res));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -410,3 +409,26 @@ fn collect_items(
|
|||
|
||||
items
|
||||
}
|
||||
|
||||
fn collect_macro_items(
|
||||
db: &dyn DefDatabase,
|
||||
module: ModuleId,
|
||||
expander: &mut Expander,
|
||||
container: ItemContainerId,
|
||||
limit: usize,
|
||||
res: ExpandResult<Option<(Mark, ast::MacroItems)>>,
|
||||
) -> Vec<(Name, AssocItemId)> {
|
||||
if let Some((mark, mac)) = res.value {
|
||||
let src: InFile<ast::MacroItems> = expander.to_source(mac);
|
||||
let tree_id = item_tree::TreeId::new(src.file_id, None);
|
||||
let item_tree = tree_id.item_tree(db);
|
||||
let iter = item_tree.top_level_items().iter().filter_map(ModItem::as_assoc_item);
|
||||
let items = collect_items(db, module, expander, iter, tree_id, container, limit - 1);
|
||||
|
||||
expander.exit(db, mark);
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
Vec::new()
|
||||
}
|
||||
|
|
|
@ -920,6 +920,17 @@ impl From<AssocItem> for ModItem {
|
|||
}
|
||||
}
|
||||
|
||||
impl AssocItem {
|
||||
pub fn ast_id(self, tree: &ItemTree) -> FileAstId<ast::AssocItem> {
|
||||
match self {
|
||||
AssocItem::Function(id) => tree[id].ast_id.upcast(),
|
||||
AssocItem::TypeAlias(id) => tree[id].ast_id.upcast(),
|
||||
AssocItem::Const(id) => tree[id].ast_id.upcast(),
|
||||
AssocItem::MacroCall(id) => tree[id].ast_id.upcast(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Variant {
|
||||
pub name: Name,
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
//! path and, upon success, we run macro expansion and "collect module" phase on
|
||||
//! the result
|
||||
|
||||
pub mod attr_resolution;
|
||||
pub mod diagnostics;
|
||||
mod collector;
|
||||
mod mod_resolution;
|
||||
|
@ -64,7 +65,7 @@ use la_arena::Arena;
|
|||
use profile::Count;
|
||||
use rustc_hash::FxHashMap;
|
||||
use stdx::format_to;
|
||||
use syntax::ast;
|
||||
use syntax::{ast, SmolStr};
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
|
@ -107,6 +108,11 @@ pub struct DefMap {
|
|||
/// (the primary purpose is to resolve derive helpers and fetch a proc-macros name)
|
||||
exported_proc_macros: FxHashMap<MacroDefId, ProcMacroDef>,
|
||||
|
||||
/// Custom attributes registered with `#![register_attr]`.
|
||||
registered_attrs: Vec<SmolStr>,
|
||||
/// Custom tool modules registered with `#![register_tool]`.
|
||||
registered_tools: Vec<SmolStr>,
|
||||
|
||||
edition: Edition,
|
||||
diagnostics: Vec<DefDiagnostic>,
|
||||
}
|
||||
|
@ -271,6 +277,8 @@ impl DefMap {
|
|||
prelude: None,
|
||||
root,
|
||||
modules,
|
||||
registered_attrs: Vec::new(),
|
||||
registered_tools: Vec::new(),
|
||||
diagnostics: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -443,6 +451,8 @@ impl DefMap {
|
|||
extern_prelude,
|
||||
diagnostics,
|
||||
modules,
|
||||
registered_attrs,
|
||||
registered_tools,
|
||||
block: _,
|
||||
edition: _,
|
||||
krate: _,
|
||||
|
@ -454,6 +464,8 @@ impl DefMap {
|
|||
exported_proc_macros.shrink_to_fit();
|
||||
diagnostics.shrink_to_fit();
|
||||
modules.shrink_to_fit();
|
||||
registered_attrs.shrink_to_fit();
|
||||
registered_tools.shrink_to_fit();
|
||||
for (_, module) in modules.iter_mut() {
|
||||
module.children.shrink_to_fit();
|
||||
module.scope.shrink_to_fit();
|
||||
|
|
90
crates/hir_def/src/nameres/attr_resolution.rs
Normal file
90
crates/hir_def/src/nameres/attr_resolution.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
//! Post-nameres attribute resolution.
|
||||
|
||||
use hir_expand::MacroCallId;
|
||||
use syntax::{ast, SmolStr};
|
||||
|
||||
use crate::{
|
||||
attr::Attr,
|
||||
attr_macro_as_call_id, builtin_attr,
|
||||
db::DefDatabase,
|
||||
item_scope::BuiltinShadowMode,
|
||||
nameres::path_resolution::ResolveMode,
|
||||
path::{ModPath, PathKind},
|
||||
AstIdWithPath, LocalModuleId, UnresolvedMacro,
|
||||
};
|
||||
|
||||
use super::DefMap;
|
||||
|
||||
pub enum ResolvedAttr {
|
||||
/// Attribute resolved to an attribute macro.
|
||||
Macro(MacroCallId),
|
||||
/// Attribute resolved to something else that does not require expansion.
|
||||
Other,
|
||||
}
|
||||
|
||||
impl DefMap {
|
||||
pub(crate) fn resolve_attr_macro(
|
||||
&self,
|
||||
db: &dyn DefDatabase,
|
||||
original_module: LocalModuleId,
|
||||
ast_id: AstIdWithPath<ast::Item>,
|
||||
attr: &Attr,
|
||||
) -> Result<ResolvedAttr, UnresolvedMacro> {
|
||||
// NB: does not currently work for derive helpers as they aren't recorded in the `DefMap`
|
||||
|
||||
if self.is_builtin_or_registered_attr(&ast_id.path) {
|
||||
return Ok(ResolvedAttr::Other);
|
||||
}
|
||||
|
||||
let resolved_res = self.resolve_path_fp_with_macro(
|
||||
db,
|
||||
ResolveMode::Other,
|
||||
original_module,
|
||||
&ast_id.path,
|
||||
BuiltinShadowMode::Module,
|
||||
);
|
||||
let def = match resolved_res.resolved_def.take_macros() {
|
||||
Some(def) => {
|
||||
if def.is_attribute() {
|
||||
def
|
||||
} else {
|
||||
return Ok(ResolvedAttr::Other);
|
||||
}
|
||||
}
|
||||
None => return Err(UnresolvedMacro { path: ast_id.path.clone() }),
|
||||
};
|
||||
|
||||
Ok(ResolvedAttr::Macro(attr_macro_as_call_id(&ast_id, attr, db, self.krate, def)))
|
||||
}
|
||||
|
||||
pub(crate) fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
|
||||
if path.kind != PathKind::Plain {
|
||||
return false;
|
||||
}
|
||||
|
||||
let segments = path.segments();
|
||||
|
||||
if let Some(name) = segments.first() {
|
||||
let name = name.to_smol_str();
|
||||
let pred = |n: &_| *n == name;
|
||||
|
||||
let registered = self.registered_tools.iter().map(SmolStr::as_str);
|
||||
let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
|
||||
// FIXME: tool modules can be shadowed by actual modules
|
||||
if is_tool {
|
||||
return true;
|
||||
}
|
||||
|
||||
if segments.len() == 1 {
|
||||
let registered = self.registered_attrs.iter().map(SmolStr::as_str);
|
||||
let is_inert = builtin_attr::INERT_ATTRIBUTES
|
||||
.iter()
|
||||
.map(|it| it.name)
|
||||
.chain(registered)
|
||||
.any(pred);
|
||||
return is_inert;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
|
@ -20,11 +20,11 @@ use itertools::Itertools;
|
|||
use la_arena::Idx;
|
||||
use limit::Limit;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use syntax::{ast, SmolStr};
|
||||
use syntax::ast;
|
||||
|
||||
use crate::{
|
||||
attr::{Attr, AttrId, AttrInput, Attrs},
|
||||
attr_macro_as_call_id, builtin_attr,
|
||||
attr_macro_as_call_id,
|
||||
db::DefDatabase,
|
||||
derive_macro_as_call_id,
|
||||
intern::Interned,
|
||||
|
@ -97,8 +97,6 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
|
|||
from_glob_import: Default::default(),
|
||||
skip_attrs: Default::default(),
|
||||
derive_helpers_in_scope: Default::default(),
|
||||
registered_attrs: Default::default(),
|
||||
registered_tools: Default::default(),
|
||||
};
|
||||
if tree_id.is_block() {
|
||||
collector.seed_with_inner(tree_id);
|
||||
|
@ -251,10 +249,6 @@ struct DefCollector<'a> {
|
|||
/// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
|
||||
/// attributes.
|
||||
derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<Name>>,
|
||||
/// Custom attributes registered with `#![register_attr]`.
|
||||
registered_attrs: Vec<SmolStr>,
|
||||
/// Custom tool modules registered with `#![register_tool]`.
|
||||
registered_tools: Vec<SmolStr>,
|
||||
}
|
||||
|
||||
impl DefCollector<'_> {
|
||||
|
@ -291,10 +285,10 @@ impl DefCollector<'_> {
|
|||
};
|
||||
|
||||
if *attr_name == hir_expand::name![register_attr] {
|
||||
self.registered_attrs.push(registered_name.to_smol_str());
|
||||
self.def_map.registered_attrs.push(registered_name.to_smol_str());
|
||||
cov_mark::hit!(register_attr);
|
||||
} else {
|
||||
self.registered_tools.push(registered_name.to_smol_str());
|
||||
self.def_map.registered_tools.push(registered_name.to_smol_str());
|
||||
cov_mark::hit!(register_tool);
|
||||
}
|
||||
}
|
||||
|
@ -1791,7 +1785,7 @@ impl ModCollector<'_, '_> {
|
|||
});
|
||||
|
||||
for attr in iter {
|
||||
if self.is_builtin_or_registered_attr(&attr.path) {
|
||||
if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) {
|
||||
continue;
|
||||
}
|
||||
tracing::debug!("non-builtin attribute {}", attr.path);
|
||||
|
@ -1819,37 +1813,6 @@ impl ModCollector<'_, '_> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn is_builtin_or_registered_attr(&self, path: &ModPath) -> bool {
|
||||
if path.kind != PathKind::Plain {
|
||||
return false;
|
||||
}
|
||||
|
||||
let segments = path.segments();
|
||||
|
||||
if let Some(name) = segments.first() {
|
||||
let name = name.to_smol_str();
|
||||
let pred = |n: &_| *n == name;
|
||||
|
||||
let registered = self.def_collector.registered_tools.iter().map(SmolStr::as_str);
|
||||
let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
|
||||
// FIXME: tool modules can be shadowed by actual modules
|
||||
if is_tool {
|
||||
return true;
|
||||
}
|
||||
|
||||
if segments.len() == 1 {
|
||||
let registered = self.def_collector.registered_attrs.iter().map(SmolStr::as_str);
|
||||
let is_inert = builtin_attr::INERT_ATTRIBUTES
|
||||
.iter()
|
||||
.map(|it| it.name)
|
||||
.chain(registered)
|
||||
.any(pred);
|
||||
return is_inert;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// If `attrs` registers a procedural macro, collects its definition.
|
||||
fn collect_proc_macro_def(&mut self, func_name: &Name, ast_id: AstId<ast::Fn>, attrs: &Attrs) {
|
||||
// FIXME: this should only be done in the root module of `proc-macro` crates, not everywhere
|
||||
|
@ -2101,8 +2064,6 @@ mod tests {
|
|||
from_glob_import: Default::default(),
|
||||
skip_attrs: Default::default(),
|
||||
derive_helpers_in_scope: Default::default(),
|
||||
registered_attrs: Default::default(),
|
||||
registered_tools: Default::default(),
|
||||
};
|
||||
collector.seed_with_top_level();
|
||||
collector.collect();
|
||||
|
|
|
@ -108,6 +108,17 @@ impl HasName for Macro {
|
|||
|
||||
impl HasAttrs for Macro {}
|
||||
|
||||
impl From<ast::AssocItem> for ast::Item {
|
||||
fn from(assoc: ast::AssocItem) -> Self {
|
||||
match assoc {
|
||||
ast::AssocItem::Const(it) => ast::Item::Const(it),
|
||||
ast::AssocItem::Fn(it) => ast::Item::Fn(it),
|
||||
ast::AssocItem::MacroCall(it) => ast::Item::MacroCall(it),
|
||||
ast::AssocItem::TypeAlias(it) => ast::Item::TypeAlias(it),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AttrKind {
|
||||
Inner,
|
||||
|
|
Loading…
Reference in a new issue