mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-04 19:29:07 +00:00
Expand derive macros under cursor in Expand Macro Recursively
This commit is contained in:
parent
6287d388c0
commit
d99b81f839
@ -148,6 +148,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
|||||||
self.imp.expand_attr_macro(item)
|
self.imp.expand_attr_macro(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expand_derive_macro(&self, derive: &ast::Attr) -> Option<SyntaxNode> {
|
||||||
|
self.imp.expand_derive_macro(derive)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
|
pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
|
||||||
self.imp.is_attr_macro_call(item)
|
self.imp.is_attr_macro_call(item)
|
||||||
}
|
}
|
||||||
@ -385,6 +389,18 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
Some(node)
|
Some(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expand_derive_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
|
||||||
|
let item = attr.syntax().parent().and_then(ast::Item::cast)?;
|
||||||
|
let sa = self.analyze(item.syntax());
|
||||||
|
let item = InFile::new(sa.file_id, &item);
|
||||||
|
let src = InFile::new(sa.file_id, attr.clone());
|
||||||
|
let macro_call_id = self.with_ctx(|ctx| ctx.attr_to_derive_macro_call(item, src))?;
|
||||||
|
let file_id = macro_call_id.as_file();
|
||||||
|
let node = self.db.parse_or_expand(file_id)?;
|
||||||
|
self.cache(node.clone(), file_id);
|
||||||
|
Some(node)
|
||||||
|
}
|
||||||
|
|
||||||
fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
|
fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
|
||||||
let sa = self.analyze(item.syntax());
|
let sa = self.analyze(item.syntax());
|
||||||
let src = InFile::new(sa.file_id, item.clone());
|
let src = InFile::new(sa.file_id, item.clone());
|
||||||
|
@ -242,6 +242,15 @@ impl SourceToDefCtx<'_, '_> {
|
|||||||
map[keys::ATTR_MACRO].get(&src).copied()
|
map[keys::ATTR_MACRO].get(&src).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn attr_to_derive_macro_call(
|
||||||
|
&mut self,
|
||||||
|
item: InFile<&ast::Item>,
|
||||||
|
src: InFile<ast::Attr>,
|
||||||
|
) -> Option<MacroCallId> {
|
||||||
|
let map = self.dyn_map(item)?;
|
||||||
|
map[keys::DERIVE_MACRO].get(&src).copied()
|
||||||
|
}
|
||||||
|
|
||||||
fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
|
fn to_def<Ast: AstNode + 'static, ID: Copy + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
src: InFile<Ast>,
|
src: InFile<Ast>,
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_expand::HirFileId;
|
use hir_expand::HirFileId;
|
||||||
|
use syntax::ast::AttrsOwner;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
@ -108,6 +109,12 @@ impl ChildBySource for ItemScope {
|
|||||||
let item = ast_id.with_value(ast_id.to_node(db.upcast()));
|
let item = ast_id.with_value(ast_id.to_node(db.upcast()));
|
||||||
res[keys::ATTR_MACRO].insert(item, call_id);
|
res[keys::ATTR_MACRO].insert(item, call_id);
|
||||||
});
|
});
|
||||||
|
self.derive_macro_invocs().for_each(|(ast_id, (attr_id, call_id))| {
|
||||||
|
let item = ast_id.to_node(db.upcast());
|
||||||
|
if let Some(attr) = item.attrs().nth(attr_id.ast_index as usize) {
|
||||||
|
res[keys::DERIVE_MACRO].insert(ast_id.with_value(attr), call_id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
fn add_module_def(
|
fn add_module_def(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
|
@ -12,8 +12,8 @@ use stdx::format_to;
|
|||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId, ImplId,
|
attr::AttrId, db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType,
|
||||||
LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId,
|
ConstId, ImplId, LocalModuleId, MacroDefId, ModuleDefId, ModuleId, TraitId,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
@ -61,6 +61,7 @@ pub struct ItemScope {
|
|||||||
// be all resolved to the last one defined if shadowing happens.
|
// be all resolved to the last one defined if shadowing happens.
|
||||||
legacy_macros: FxHashMap<Name, MacroDefId>,
|
legacy_macros: FxHashMap<Name, MacroDefId>,
|
||||||
attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>,
|
attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>,
|
||||||
|
derive_macros: FxHashMap<AstId<ast::Item>, (AttrId, MacroCallId)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
|
pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
|
||||||
@ -182,6 +183,21 @@ impl ItemScope {
|
|||||||
self.attr_macros.iter().map(|(k, v)| (*k, *v))
|
self.attr_macros.iter().map(|(k, v)| (*k, *v))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_derive_macro_invoc(
|
||||||
|
&mut self,
|
||||||
|
item: AstId<ast::Item>,
|
||||||
|
call: MacroCallId,
|
||||||
|
attr_id: AttrId,
|
||||||
|
) {
|
||||||
|
self.derive_macros.insert(item, (attr_id, call));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn derive_macro_invocs(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = (AstId<ast::Item>, (AttrId, MacroCallId))> + '_ {
|
||||||
|
self.derive_macros.iter().map(|(k, v)| (*k, *v))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
|
pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
|
||||||
self.unnamed_trait_imports.get(&tr).copied()
|
self.unnamed_trait_imports.get(&tr).copied()
|
||||||
}
|
}
|
||||||
@ -320,6 +336,7 @@ impl ItemScope {
|
|||||||
unnamed_trait_imports,
|
unnamed_trait_imports,
|
||||||
legacy_macros,
|
legacy_macros,
|
||||||
attr_macros,
|
attr_macros,
|
||||||
|
derive_macros,
|
||||||
} = self;
|
} = self;
|
||||||
types.shrink_to_fit();
|
types.shrink_to_fit();
|
||||||
values.shrink_to_fit();
|
values.shrink_to_fit();
|
||||||
@ -331,6 +348,7 @@ impl ItemScope {
|
|||||||
unnamed_trait_imports.shrink_to_fit();
|
unnamed_trait_imports.shrink_to_fit();
|
||||||
legacy_macros.shrink_to_fit();
|
legacy_macros.shrink_to_fit();
|
||||||
attr_macros.shrink_to_fit();
|
attr_macros.shrink_to_fit();
|
||||||
|
derive_macros.shrink_to_fit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
|
|||||||
|
|
||||||
pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
|
pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
|
||||||
pub const ATTR_MACRO: Key<ast::Item, MacroCallId> = Key::new();
|
pub const ATTR_MACRO: Key<ast::Item, MacroCallId> = Key::new();
|
||||||
|
pub const DERIVE_MACRO: Key<ast::Attr, MacroCallId> = Key::new();
|
||||||
|
|
||||||
/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
|
/// XXX: AST Nodes and SyntaxNodes have identity equality semantics: nodes are
|
||||||
/// equal if they point to exactly the same object.
|
/// equal if they point to exactly the same object.
|
||||||
|
@ -1047,6 +1047,12 @@ impl DefCollector<'_> {
|
|||||||
&resolver,
|
&resolver,
|
||||||
) {
|
) {
|
||||||
Ok(call_id) => {
|
Ok(call_id) => {
|
||||||
|
self.def_map.modules[directive.module_id].scope.add_derive_macro_invoc(
|
||||||
|
ast_id.ast_id,
|
||||||
|
call_id,
|
||||||
|
*derive_attr,
|
||||||
|
);
|
||||||
|
|
||||||
resolved.push((directive.module_id, call_id, directive.depth));
|
resolved.push((directive.module_id, call_id, directive.depth));
|
||||||
res = ReachedFixedPoint::No;
|
res = ReachedFixedPoint::No;
|
||||||
return false;
|
return false;
|
||||||
|
@ -2,6 +2,7 @@ use std::iter;
|
|||||||
|
|
||||||
use hir::Semantics;
|
use hir::Semantics;
|
||||||
use ide_db::{helpers::pick_best_token, RootDatabase};
|
use ide_db::{helpers::pick_best_token, RootDatabase};
|
||||||
|
use itertools::Itertools;
|
||||||
use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T};
|
use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxKind::*, SyntaxNode, WalkEvent, T};
|
||||||
|
|
||||||
use crate::FilePosition;
|
use crate::FilePosition;
|
||||||
@ -33,6 +34,18 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
|
|||||||
let mut expanded = None;
|
let mut expanded = None;
|
||||||
let mut name = None;
|
let mut name = None;
|
||||||
for node in tok.ancestors() {
|
for node in tok.ancestors() {
|
||||||
|
if let Some(attr) = ast::Attr::cast(node.clone()) {
|
||||||
|
if let Some((path, tt)) = attr.as_simple_call() {
|
||||||
|
if path == "derive" {
|
||||||
|
let mut tt = tt.syntax().children_with_tokens().skip(1).join("");
|
||||||
|
tt.pop();
|
||||||
|
name = Some(tt);
|
||||||
|
expanded = sema.expand_derive_macro(&attr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(item) = ast::Item::cast(node.clone()) {
|
if let Some(item) = ast::Item::cast(node.clone()) {
|
||||||
if let Some(def) = sema.resolve_attr_macro_call(&item) {
|
if let Some(def) = sema.resolve_attr_macro_call(&item) {
|
||||||
name = def.name(db).map(|name| name.to_string());
|
name = def.name(db).map(|name| name.to_string());
|
||||||
@ -325,4 +338,22 @@ fn main() {
|
|||||||
0 "#]],
|
0 "#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_expand_derive() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
|
||||||
|
#[rustc_builtin_macro]
|
||||||
|
pub macro Clone {}
|
||||||
|
|
||||||
|
#[derive(C$0lone)]
|
||||||
|
struct Foo {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
Clone
|
||||||
|
impl< >crate::clone::Clone for Foo< >{}
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user