Basic support for decl macros 2.0

This commit is contained in:
Jonas Schievink 2020-12-15 18:43:19 +01:00
parent bd4c352831
commit c31c3246a8
15 changed files with 195 additions and 28 deletions

2
Cargo.lock generated
View file

@ -1828,8 +1828,6 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "ungrammar"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "873186a460627379e7e28880a0d33b729c205634f6f021321f50b323235e62d7"
[[package]]
name = "unicase"

View file

@ -26,4 +26,4 @@ debug = 0 # Set this to 1 or 2 to get more useful backtraces in debugger.
# chalk-ir = { path = "../chalk/chalk-ir" }
# chalk-recursive = { path = "../chalk/chalk-recursive" }
# ungrammar = { path = "../ungrammar" }
ungrammar = { path = "../ungrammar" }

View file

@ -110,8 +110,8 @@ impl HasSource for TypeAlias {
}
}
impl HasSource for MacroDef {
type Ast = ast::MacroRules;
fn source(self, db: &dyn HirDatabase) -> InFile<ast::MacroRules> {
type Ast = ast::Macro;
fn source(self, db: &dyn HirDatabase) -> InFile<ast::Macro> {
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()),

View file

@ -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 })
}

View file

@ -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)));
}
};

View file

@ -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<Mod>,
macro_calls: Arena<MacroCall>,
macro_rules: Arena<MacroRules>,
macro_defs: Arena<MacroDef>,
exprs: Arena<Expr>,
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<ast::MacroRules>,
}
/// "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<ast::MacroDef>,
}
// 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(),
}
}
}

View file

@ -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::<SmallVec<_>>()))
}
@ -561,6 +563,18 @@ impl Ctx {
Some(id(self.data().macro_rules.alloc(res)))
}
fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option<FileItemTreeId<MacroDef>> {
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<ModItem> {
block.extern_item_list().map_or(Vec::new(), |list| {
list.extern_items()

View file

@ -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 {

View file

@ -63,7 +63,7 @@ macro_rules! register_builtin {
pub fn find_builtin_macro(
ident: &name::Name,
krate: CrateId,
ast_id: AstId<ast::MacroRules>,
ast_id: AstId<ast::Macro>,
) -> Option<MacroDefId> {
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(&macro_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(&macro_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(&macro_rules[0]))),
ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_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(&macro_calls[0]),
ast_id_map.ast_id(&macro_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(&macro_rules[0]))),
ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_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({

View file

@ -129,7 +129,10 @@ fn ast_id_map(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
fn macro_def(db: &dyn AstDatabase, id: MacroDefId) -> Option<Arc<(TokenExpander, mbe::TokenMap)>> {
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);

View file

@ -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<CrateId>,
pub ast_id: Option<AstId<ast::MacroRules>>,
pub ast_id: Option<AstId<ast::Macro>>,
pub kind: MacroDefKind,
pub local_inner: bool,

View file

@ -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::*,

View file

@ -286,6 +286,18 @@ impl MacroRules {
pub fn token_tree(&self) -> Option<TokenTree> { 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<SyntaxToken> { support::token(&self.syntax, T![macro]) }
pub fn args(&self) -> Option<TokenTree> { support::child(&self.syntax) }
pub fn body(&self) -> Option<TokenTree> { 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<Self> {
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<Self> {
@ -3086,6 +3110,9 @@ impl From<MacroCall> for Item {
impl From<MacroRules> for Item {
fn from(node: MacroRules) -> Item { Item::MacroRules(node) }
}
impl From<MacroDef> for Item {
fn from(node: MacroDef) -> Item { Item::MacroDef(node) }
}
impl From<Module> 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)

View file

@ -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<ast::MacroRules> for Macro {
fn from(it: ast::MacroRules) -> Self {
Macro::MacroRules(it)
}
}
impl From<ast::MacroDef> 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<Self> {
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<ast::Name> {
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 {}

View file

@ -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
}
}
}