From c31c3246a8c87a3639623c30b692a57e728bb046 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 15 Dec 2020 18:43:19 +0100 Subject: Basic support for decl macros 2.0 --- crates/hir/src/has_source.rs | 4 +-- crates/hir/src/semantics/source_to_def.rs | 2 +- crates/hir_def/src/body/lower.rs | 5 ++- crates/hir_def/src/item_tree.rs | 20 ++++++++++-- crates/hir_def/src/item_tree/lower.rs | 16 ++++++++- crates/hir_def/src/nameres/collector.rs | 29 ++++++++++++++++- crates/hir_expand/src/builtin_macro.rs | 19 ++++++----- crates/hir_expand/src/db.rs | 5 ++- crates/hir_expand/src/lib.rs | 7 ++-- crates/syntax/src/ast.rs | 4 +-- crates/syntax/src/ast/generated/nodes.rs | 36 ++++++++++++++++++++- crates/syntax/src/ast/node_ext.rs | 54 +++++++++++++++++++++++++++++++ crates/syntax/src/display.rs | 18 +++++++++-- 13 files changed, 194 insertions(+), 25 deletions(-) (limited to 'crates') diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index 107ad0690..ecf3194c6 100644 --- a/crates/hir/src/has_source.rs +++ b/crates/hir/src/has_source.rs @@ -110,8 +110,8 @@ impl HasSource for TypeAlias { } } impl HasSource for MacroDef { - type Ast = ast::MacroRules; - fn source(self, db: &dyn HirDatabase) -> InFile { + type Ast = ast::Macro; + fn source(self, db: &dyn HirDatabase) -> InFile { InFile { file_id: self.id.ast_id.expect("MacroDef without ast_id").file_id, value: self.id.ast_id.expect("MacroDef without ast_id").to_node(db.upcast()), diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index a333d7aea..d499ae340 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -157,7 +157,7 @@ impl SourceToDefCtx<'_, '_> { let file_id = src.file_id.original_file(self.db.upcast()); let krate = self.file_to_def(file_id)?.krate; let file_ast_id = self.db.ast_id_map(src.file_id).ast_id(&src.value); - let ast_id = Some(AstId::new(src.file_id, file_ast_id)); + let ast_id = Some(AstId::new(src.file_id, file_ast_id.upcast())); Some(MacroDefId { krate: Some(krate), ast_id, kind, local_inner: false }) } diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index bdba4c33e..e4bf5603c 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -772,7 +772,10 @@ impl ExprCollector<'_> { | ast::Item::Module(_) | ast::Item::MacroCall(_) => return None, ast::Item::MacroRules(def) => { - return Some(Either::Right(def)); + return Some(Either::Right(ast::Macro::from(def))); + } + ast::Item::MacroDef(def) => { + return Some(Either::Right(ast::Macro::from(def))); } }; diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 1c9babf37..8cd0b18cc 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs @@ -143,6 +143,7 @@ impl ItemTree { mods, macro_calls, macro_rules, + macro_defs, exprs, vis, generics, @@ -164,6 +165,7 @@ impl ItemTree { mods.shrink_to_fit(); macro_calls.shrink_to_fit(); macro_rules.shrink_to_fit(); + macro_defs.shrink_to_fit(); exprs.shrink_to_fit(); vis.arena.shrink_to_fit(); @@ -283,6 +285,7 @@ struct ItemTreeData { mods: Arena, macro_calls: Arena, macro_rules: Arena, + macro_defs: Arena, exprs: Arena, vis: ItemVisibilities, @@ -431,6 +434,7 @@ mod_items! { Mod in mods -> ast::Module, MacroCall in macro_calls -> ast::MacroCall, MacroRules in macro_rules -> ast::MacroRules, + MacroDef in macro_defs -> ast::MacroDef, } macro_rules! impl_index { @@ -640,7 +644,7 @@ pub struct MacroCall { #[derive(Debug, Clone, Eq, PartialEq)] pub struct MacroRules { - /// For `macro_rules!` declarations, this is the name of the declared macro. + /// The name of the declared macro. pub name: Name, /// Has `#[macro_export]`. pub is_export: bool, @@ -651,6 +655,16 @@ pub struct MacroRules { pub ast_id: FileAstId, } +/// "Macros 2.0" macro definition. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct MacroDef { + pub name: Name, + pub visibility: RawVisibilityId, + /// Has `#[rustc_builtin_macro]`. + pub is_builtin: bool, + pub ast_id: FileAstId, +} + // NB: There's no `FileAstId` for `Expr`. The only case where this would be useful is for array // lengths, but we don't do much with them yet. #[derive(Debug, Clone, Eq, PartialEq)] @@ -680,7 +694,8 @@ impl ModItem { | ModItem::Trait(_) | ModItem::Impl(_) | ModItem::Mod(_) - | ModItem::MacroRules(_) => None, + | ModItem::MacroRules(_) + | ModItem::MacroDef(_) => None, ModItem::MacroCall(call) => Some(AssocItem::MacroCall(*call)), ModItem::Const(konst) => Some(AssocItem::Const(*konst)), ModItem::TypeAlias(alias) => Some(AssocItem::TypeAlias(*alias)), @@ -708,6 +723,7 @@ impl ModItem { ModItem::Mod(it) => tree[it.index].ast_id().upcast(), ModItem::MacroCall(it) => tree[it.index].ast_id().upcast(), ModItem::MacroRules(it) => tree[it.index].ast_id().upcast(), + ModItem::MacroDef(it) => tree[it.index].ast_id().upcast(), } } } diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index b39d7fb7a..1dc06a211 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs @@ -101,7 +101,8 @@ impl Ctx { | ast::Item::ExternCrate(_) | ast::Item::Use(_) | ast::Item::MacroCall(_) - | ast::Item::MacroRules(_) => {} + | ast::Item::MacroRules(_) + | ast::Item::MacroDef(_) => {} }; let attrs = Attrs::new(item, &self.hygiene); @@ -122,6 +123,7 @@ impl Ctx { ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast).map(Into::into), ast::Item::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into), ast::Item::MacroRules(ast) => self.lower_macro_rules(ast).map(Into::into), + ast::Item::MacroDef(ast) => self.lower_macro_def(ast).map(Into::into), ast::Item::ExternBlock(ast) => { Some(ModItems(self.lower_extern_block(ast).into_iter().collect::>())) } @@ -561,6 +563,18 @@ impl Ctx { Some(id(self.data().macro_rules.alloc(res))) } + fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option> { + let name = m.name().map(|it| it.as_name())?; + let attrs = Attrs::new(m, &self.hygiene); + + let ast_id = self.source_ast_id_map.ast_id(m); + let visibility = self.lower_visibility(m); + + let is_builtin = attrs.by_key("rustc_builtin_macro").exists(); + let res = MacroDef { name, is_builtin, ast_id, visibility }; + Some(id(self.data().macro_defs.alloc(res))) + } + fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> Vec { block.extern_item_list().map_or(Vec::new(), |list| { list.extern_items() diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 85cc342c4..c2f741060 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -976,6 +976,33 @@ impl ModCollector<'_, '_> { } ModItem::MacroCall(mac) => self.collect_macro_call(&self.item_tree[mac]), ModItem::MacroRules(mac) => self.collect_macro_rules(&self.item_tree[mac]), + ModItem::MacroDef(id) => { + let mac = &self.item_tree[id]; + let ast_id = InFile::new(self.file_id, mac.ast_id.upcast()); + + // "Macro 2.0" is not currently supported by rust-analyzer, but libcore uses it + // to define builtin macros, so we support at least that part. + if mac.is_builtin { + let krate = self.def_collector.def_map.krate; + if let Some(macro_id) = find_builtin_macro(&mac.name, krate, ast_id) { + let vis = self + .def_collector + .def_map + .resolve_visibility( + self.def_collector.db, + self.module_id, + &self.item_tree[mac.visibility], + ) + .unwrap_or(Visibility::Public); + self.def_collector.update( + self.module_id, + &[(Some(mac.name.clone()), PerNs::macros(macro_id, vis))], + vis, + ImportType::Named, + ); + } + } + } ModItem::Impl(imp) => { let module = ModuleId { krate: self.def_collector.def_map.krate, @@ -1280,7 +1307,7 @@ impl ModCollector<'_, '_> { } fn collect_macro_rules(&mut self, mac: &MacroRules) { - let ast_id = InFile::new(self.file_id, mac.ast_id); + let ast_id = InFile::new(self.file_id, mac.ast_id.upcast()); // Case 1: builtin macros if mac.is_builtin { diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index bd9223825..df82cf8e6 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs @@ -63,7 +63,7 @@ macro_rules! register_builtin { pub fn find_builtin_macro( ident: &name::Name, krate: CrateId, - ast_id: AstId, + ast_id: AstId, ) -> Option { let kind = find_by_name(ident)?; @@ -515,16 +515,19 @@ mod tests { fn expand_builtin_macro(ra_fixture: &str) -> String { let (db, file_id) = TestDB::with_single_file(&ra_fixture); let parsed = db.parse(file_id); - let macro_rules: Vec<_> = + let mut macro_rules: Vec<_> = parsed.syntax_node().descendants().filter_map(ast::MacroRules::cast).collect(); - let macro_calls: Vec<_> = + let mut macro_calls: Vec<_> = parsed.syntax_node().descendants().filter_map(ast::MacroCall::cast).collect(); let ast_id_map = db.ast_id_map(file_id.into()); assert_eq!(macro_rules.len(), 1, "test must contain exactly 1 `macro_rules!`"); assert_eq!(macro_calls.len(), 1, "test must contain exactly 1 macro call"); - let expander = find_by_name(¯o_rules[0].name().unwrap().as_name()).unwrap(); + let macro_rules = ast::Macro::from(macro_rules.pop().unwrap()); + let macro_call = macro_calls.pop().unwrap(); + + let expander = find_by_name(¯o_rules.name().unwrap().as_name()).unwrap(); let krate = CrateId(0); let file_id = match expander { @@ -532,7 +535,7 @@ mod tests { // the first one should be a macro_rules let def = MacroDefId { krate: Some(CrateId(0)), - ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules[0]))), + ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules))), kind: MacroDefKind::BuiltIn(expander), local_inner: false, }; @@ -542,7 +545,7 @@ mod tests { krate, kind: MacroCallKind::FnLike(AstId::new( file_id.into(), - ast_id_map.ast_id(¯o_calls[0]), + ast_id_map.ast_id(¯o_call), )), }; @@ -553,12 +556,12 @@ mod tests { // the first one should be a macro_rules let def = MacroDefId { krate: Some(krate), - ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules[0]))), + ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(¯o_rules))), kind: MacroDefKind::BuiltInEager(expander), local_inner: false, }; - let args = macro_calls[0].token_tree().unwrap(); + let args = macro_call.token_tree().unwrap(); let parsed_args = mbe::ast_to_token_tree(&args).unwrap().0; let arg_id = db.intern_eager_expansion({ diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index 11b5b98c8..4477d867f 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs @@ -129,7 +129,10 @@ fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc { fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option> { match id.kind { MacroDefKind::Declarative => { - let macro_call = id.ast_id?.to_node(db); + let macro_call = match id.ast_id?.to_node(db) { + syntax::ast::Macro::MacroRules(mac) => mac, + syntax::ast::Macro::MacroDef(_) => return None, + }; let arg = macro_call.token_tree()?; let (tt, tmap) = mbe::ast_to_token_tree(&arg).or_else(|| { log::warn!("fail on macro_def to token tree: {:#?}", arg); diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs index ae3086a95..55f026c7b 100644 --- a/crates/hir_expand/src/lib.rs +++ b/crates/hir_expand/src/lib.rs @@ -145,7 +145,10 @@ impl HirFileId { let arg_tt = loc.kind.arg(db)?; let def = loc.def.ast_id.and_then(|id| { - let def_tt = id.to_node(db).token_tree()?; + let def_tt = match id.to_node(db) { + ast::Macro::MacroRules(mac) => mac.token_tree()?, + ast::Macro::MacroDef(_) => return None, + }; Some(InFile::new(id.file_id, def_tt)) }); @@ -228,7 +231,7 @@ pub struct MacroDefId { // (which will probably require touching this code), we can instead use // that (and also remove the hacks for resolving built-in derives). pub krate: Option, - pub ast_id: Option>, + pub ast_id: Option>, pub kind: MacroDefKind, pub local_inner: bool, diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs index 7844f9ed6..70c568ea1 100644 --- a/crates/syntax/src/ast.rs +++ b/crates/syntax/src/ast.rs @@ -19,8 +19,8 @@ pub use self::{ expr_ext::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, RangeOp}, generated::{nodes::*, tokens::*}, node_ext::{ - AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents, - StructKind, TypeBoundKind, VisibilityKind, + AttrKind, FieldKind, Macro, NameOrNameRef, PathSegmentKind, SelfParamKind, + SlicePatComponents, StructKind, TypeBoundKind, VisibilityKind, }, token_ext::*, traits::*, diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 0ad75214f..6eae323f4 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -286,6 +286,18 @@ impl MacroRules { pub fn token_tree(&self) -> Option { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct MacroDef { + pub(crate) syntax: SyntaxNode, +} +impl ast::AttrsOwner for MacroDef {} +impl ast::NameOwner for MacroDef {} +impl ast::VisibilityOwner for MacroDef {} +impl MacroDef { + pub fn macro_token(&self) -> Option { support::token(&self.syntax, T![macro]) } + pub fn args(&self) -> Option { support::child(&self.syntax) } + pub fn body(&self) -> Option { support::child(&self.syntax) } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Module { pub(crate) syntax: SyntaxNode, } @@ -1332,6 +1344,7 @@ pub enum Item { Impl(Impl), MacroCall(MacroCall), MacroRules(MacroRules), + MacroDef(MacroDef), Module(Module), Static(Static), Struct(Struct), @@ -1689,6 +1702,17 @@ impl AstNode for MacroRules { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for MacroDef { + fn can_cast(kind: SyntaxKind) -> bool { kind == MACRO_DEF } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for Module { fn can_cast(kind: SyntaxKind) -> bool { kind == MODULE } fn cast(syntax: SyntaxNode) -> Option { @@ -3086,6 +3110,9 @@ impl From for Item { impl From for Item { fn from(node: MacroRules) -> Item { Item::MacroRules(node) } } +impl From for Item { + fn from(node: MacroDef) -> Item { Item::MacroDef(node) } +} impl From for Item { fn from(node: Module) -> Item { Item::Module(node) } } @@ -3111,7 +3138,7 @@ impl AstNode for Item { fn can_cast(kind: SyntaxKind) -> bool { match kind { CONST | ENUM | EXTERN_BLOCK | EXTERN_CRATE | FN | IMPL | MACRO_CALL | MACRO_RULES - | MODULE | STATIC | STRUCT | TRAIT | TYPE_ALIAS | UNION | USE => true, + | MACRO_DEF | MODULE | STATIC | STRUCT | TRAIT | TYPE_ALIAS | UNION | USE => true, _ => false, } } @@ -3125,6 +3152,7 @@ impl AstNode for Item { IMPL => Item::Impl(Impl { syntax }), MACRO_CALL => Item::MacroCall(MacroCall { syntax }), MACRO_RULES => Item::MacroRules(MacroRules { syntax }), + MACRO_DEF => Item::MacroDef(MacroDef { syntax }), MODULE => Item::Module(Module { syntax }), STATIC => Item::Static(Static { syntax }), STRUCT => Item::Struct(Struct { syntax }), @@ -3146,6 +3174,7 @@ impl AstNode for Item { Item::Impl(it) => &it.syntax, Item::MacroCall(it) => &it.syntax, Item::MacroRules(it) => &it.syntax, + Item::MacroDef(it) => &it.syntax, Item::Module(it) => &it.syntax, Item::Static(it) => &it.syntax, Item::Struct(it) => &it.syntax, @@ -3615,6 +3644,11 @@ impl std::fmt::Display for MacroRules { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for MacroDef { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for Module { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index c59a29eab..40dec3c7f 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -3,6 +3,7 @@ use std::fmt; +use ast::AttrsOwner; use itertools::Itertools; use parser::SyntaxKind; @@ -31,6 +32,57 @@ fn text_of_first_token(node: &SyntaxNode) -> &SmolStr { node.green().children().next().and_then(|it| it.into_token()).unwrap().text() } +pub enum Macro { + MacroRules(ast::MacroRules), + MacroDef(ast::MacroDef), +} + +impl From for Macro { + fn from(it: ast::MacroRules) -> Self { + Macro::MacroRules(it) + } +} + +impl From for Macro { + fn from(it: ast::MacroDef) -> Self { + Macro::MacroDef(it) + } +} + +impl AstNode for Macro { + fn can_cast(kind: SyntaxKind) -> bool { + match kind { + SyntaxKind::MACRO_RULES | SyntaxKind::MACRO_DEF => true, + _ => false, + } + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::MACRO_RULES => Macro::MacroRules(ast::MacroRules { syntax }), + SyntaxKind::MACRO_DEF => Macro::MacroDef(ast::MacroDef { syntax }), + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + Macro::MacroRules(it) => it.syntax(), + Macro::MacroDef(it) => it.syntax(), + } + } +} + +impl NameOwner for Macro { + fn name(&self) -> Option { + match self { + Macro::MacroRules(mac) => mac.name(), + Macro::MacroDef(mac) => mac.name(), + } + } +} + +impl AttrsOwner for Macro {} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum AttrKind { Inner, @@ -462,4 +514,6 @@ impl ast::DocCommentsOwner for ast::Const {} impl ast::DocCommentsOwner for ast::TypeAlias {} impl ast::DocCommentsOwner for ast::Impl {} impl ast::DocCommentsOwner for ast::MacroRules {} +impl ast::DocCommentsOwner for ast::MacroDef {} +impl ast::DocCommentsOwner for ast::Macro {} impl ast::DocCommentsOwner for ast::Use {} diff --git a/crates/syntax/src/display.rs b/crates/syntax/src/display.rs index d33bde30c..391647fc6 100644 --- a/crates/syntax/src/display.rs +++ b/crates/syntax/src/display.rs @@ -76,8 +76,20 @@ pub fn type_label(node: &ast::TypeAlias) -> String { label.trim().to_owned() } -pub fn macro_label(node: &ast::MacroRules) -> String { +pub fn macro_label(node: &ast::Macro) -> String { let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default(); - let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" }; - format!("{}macro_rules! {}", vis, name) + match node { + ast::Macro::MacroRules(node) => { + let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" }; + format!("{}macro_rules! {}", vis, name) + } + ast::Macro::MacroDef(node) => { + let mut s = String::new(); + if let Some(vis) = node.visibility() { + format_to!(s, "{} ", vis); + } + format_to!(s, "macro {}", name); + s + } + } } -- cgit v1.2.3