Add support macros in impl blocks

This commit is contained in:
Edwin Cheng 2019-12-21 03:37:03 +08:00
parent cfc50ff160
commit ad81d1dbc1
5 changed files with 114 additions and 48 deletions

View file

@ -23,7 +23,7 @@ use crate::{
DefWithBodyId, HasModule, Lookup, ModuleDefId, ModuleId, DefWithBodyId, HasModule, Lookup, ModuleDefId, ModuleId,
}; };
struct Expander { pub(crate) struct Expander {
crate_def_map: Arc<CrateDefMap>, crate_def_map: Arc<CrateDefMap>,
current_file_id: HirFileId, current_file_id: HirFileId,
hygiene: Hygiene, hygiene: Hygiene,
@ -32,18 +32,22 @@ struct Expander {
} }
impl Expander { impl Expander {
fn new(db: &impl DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { pub(crate) fn new(
db: &impl DefDatabase,
current_file_id: HirFileId,
module: ModuleId,
) -> Expander {
let crate_def_map = db.crate_def_map(module.krate); let crate_def_map = db.crate_def_map(module.krate);
let hygiene = Hygiene::new(db, current_file_id); let hygiene = Hygiene::new(db, current_file_id);
let ast_id_map = db.ast_id_map(current_file_id); let ast_id_map = db.ast_id_map(current_file_id);
Expander { crate_def_map, current_file_id, hygiene, ast_id_map, module } Expander { crate_def_map, current_file_id, hygiene, ast_id_map, module }
} }
fn enter_expand( pub(crate) fn enter_expand<T: ast::AstNode, DB: DefDatabase>(
&mut self, &mut self,
db: &impl DefDatabase, db: &DB,
macro_call: ast::MacroCall, macro_call: ast::MacroCall,
) -> Option<(Mark, ast::Expr)> { ) -> Option<(Mark, T)> {
let ast_id = AstId::new( let ast_id = AstId::new(
self.current_file_id, self.current_file_id,
db.ast_id_map(self.current_file_id).ast_id(&macro_call), db.ast_id_map(self.current_file_id).ast_id(&macro_call),
@ -54,7 +58,7 @@ impl Expander {
let call_id = def.as_call_id(db, MacroCallKind::FnLike(ast_id)); let call_id = def.as_call_id(db, MacroCallKind::FnLike(ast_id));
let file_id = call_id.as_file(); let file_id = call_id.as_file();
if let Some(node) = db.parse_or_expand(file_id) { if let Some(node) = db.parse_or_expand(file_id) {
if let Some(expr) = ast::Expr::cast(node) { if let Some(expr) = T::cast(node) {
log::debug!("macro expansion {:#?}", expr.syntax()); log::debug!("macro expansion {:#?}", expr.syntax());
let mark = Mark { let mark = Mark {
@ -77,14 +81,14 @@ impl Expander {
None None
} }
fn exit(&mut self, db: &impl DefDatabase, mut mark: Mark) { pub(crate) fn exit(&mut self, db: &impl DefDatabase, mut mark: Mark) {
self.hygiene = Hygiene::new(db, mark.file_id); self.hygiene = Hygiene::new(db, mark.file_id);
self.current_file_id = mark.file_id; self.current_file_id = mark.file_id;
self.ast_id_map = mem::take(&mut mark.ast_id_map); self.ast_id_map = mem::take(&mut mark.ast_id_map);
mark.bomb.defuse(); mark.bomb.defuse();
} }
fn to_source<T>(&self, value: T) -> InFile<T> { pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
InFile { file_id: self.current_file_id, value } InFile { file_id: self.current_file_id, value }
} }
@ -109,7 +113,7 @@ impl Expander {
} }
} }
struct Mark { pub(crate) struct Mark {
file_id: HirFileId, file_id: HirFileId,
ast_id_map: Arc<AstIdMap>, ast_id_map: Arc<AstIdMap>,
bomb: DropBomb, bomb: DropBomb,

View file

@ -4,16 +4,16 @@ use std::sync::Arc;
use hir_expand::{ use hir_expand::{
name::{name, AsName, Name}, name::{name, AsName, Name},
AstId, AstId, InFile,
}; };
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; use ra_syntax::ast::{self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner};
use crate::{ use crate::{
db::DefDatabase, db::DefDatabase,
src::HasSource, src::HasSource,
type_ref::{Mutability, TypeRef}, type_ref::{Mutability, TypeRef},
AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, ImplId, Intern, AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule,
Lookup, StaticId, TraitId, TypeAliasId, TypeAliasLoc, ImplId, Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
}; };
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -167,46 +167,24 @@ pub struct ImplData {
impl ImplData { impl ImplData {
pub(crate) fn impl_data_query(db: &impl DefDatabase, id: ImplId) -> Arc<ImplData> { pub(crate) fn impl_data_query(db: &impl DefDatabase, id: ImplId) -> Arc<ImplData> {
let src = id.lookup(db).source(db); let impl_loc = id.lookup(db);
let items = db.ast_id_map(src.file_id); let src = impl_loc.source(db);
let target_trait = src.value.target_trait().map(TypeRef::from_ast); let target_trait = src.value.target_trait().map(TypeRef::from_ast);
let target_type = TypeRef::from_ast_opt(src.value.target_type()); let target_type = TypeRef::from_ast_opt(src.value.target_type());
let is_negative = src.value.is_negative(); let is_negative = src.value.is_negative();
let module_id = impl_loc.container.module(db);
let items = if let Some(item_list) = src.value.item_list() { let mut items = Vec::new();
item_list if let Some(item_list) = src.value.item_list() {
.impl_items() items.extend(collect_impl_items(db, item_list.impl_items(), src.file_id, id));
.map(|item_node| match item_node { items.extend(collect_impl_items_in_macros(
ast::ImplItem::FnDef(it) => { db,
let def = FunctionLoc { module_id,
container: AssocContainerId::ImplId(id), &src.with_value(item_list),
ast_id: AstId::new(src.file_id, items.ast_id(&it)), id,
} ));
.intern(db); }
def.into()
}
ast::ImplItem::ConstDef(it) => {
let def = ConstLoc {
container: AssocContainerId::ImplId(id),
ast_id: AstId::new(src.file_id, items.ast_id(&it)),
}
.intern(db);
def.into()
}
ast::ImplItem::TypeAliasDef(it) => {
let def = TypeAliasLoc {
container: AssocContainerId::ImplId(id),
ast_id: AstId::new(src.file_id, items.ast_id(&it)),
}
.intern(db);
def.into()
}
})
.collect()
} else {
Vec::new()
};
let res = ImplData { target_trait, target_type, items, is_negative }; let res = ImplData { target_trait, target_type, items, is_negative };
Arc::new(res) Arc::new(res)
@ -237,3 +215,66 @@ impl ConstData {
ConstData { name, type_ref } ConstData { name, type_ref }
} }
} }
fn collect_impl_items_in_macros(
db: &impl DefDatabase,
module_id: ModuleId,
impl_block: &InFile<ast::ItemList>,
id: ImplId,
) -> Vec<AssocItemId> {
let mut expander = Expander::new(db, impl_block.file_id, module_id);
let mut res = Vec::new();
for m in impl_block.value.syntax().children().filter_map(ast::MacroCall::cast) {
if let Some((mark, items)) = expander.enter_expand(db, m) {
let items: InFile<ast::MacroItems> = expander.to_source(items);
expander.exit(db, mark);
res.extend(collect_impl_items(
db,
items.value.items().filter_map(|it| ImplItem::cast(it.syntax().clone())),
items.file_id,
id,
));
}
}
res
}
fn collect_impl_items(
db: &impl DefDatabase,
impl_items: impl Iterator<Item = ImplItem>,
file_id: crate::HirFileId,
id: ImplId,
) -> Vec<AssocItemId> {
let items = db.ast_id_map(file_id);
impl_items
.map(|item_node| match item_node {
ast::ImplItem::FnDef(it) => {
let def = FunctionLoc {
container: AssocContainerId::ImplId(id),
ast_id: AstId::new(file_id, items.ast_id(&it)),
}
.intern(db);
def.into()
}
ast::ImplItem::ConstDef(it) => {
let def = ConstLoc {
container: AssocContainerId::ImplId(id),
ast_id: AstId::new(file_id, items.ast_id(&it)),
}
.intern(db);
def.into()
}
ast::ImplItem::TypeAliasDef(it) => {
let def = TypeAliasLoc {
container: AssocContainerId::ImplId(id),
ast_id: AstId::new(file_id, items.ast_id(&it)),
}
.intern(db);
def.into()
}
})
.collect()
}

View file

@ -47,6 +47,7 @@ use ra_arena::{impl_arena_id, RawId};
use ra_db::{impl_intern_key, salsa, CrateId}; use ra_db::{impl_intern_key, salsa, CrateId};
use ra_syntax::{ast, AstNode}; use ra_syntax::{ast, AstNode};
use crate::body::Expander;
use crate::builtin_type::BuiltinType; use crate::builtin_type::BuiltinType;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]

View file

@ -188,6 +188,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, macro_call_id: MacroCallId) -> Fragmen
ARG_LIST => FragmentKind::Expr, ARG_LIST => FragmentKind::Expr,
TRY_EXPR => FragmentKind::Expr, TRY_EXPR => FragmentKind::Expr,
TUPLE_EXPR => FragmentKind::Expr, TUPLE_EXPR => FragmentKind::Expr,
ITEM_LIST => FragmentKind::Items,
_ => { _ => {
// Unknown , Just guess it is `Items` // Unknown , Just guess it is `Items`
FragmentKind::Items FragmentKind::Items

View file

@ -182,6 +182,25 @@ fn test() { S.foo()<|>; }
assert_eq!(t, "u128"); assert_eq!(t, "u128");
} }
#[test]
fn infer_impl_items_generated_by_macros() {
let t = type_at(
r#"
//- /main.rs
macro_rules! m {
() => (fn foo(&self) -> u128 {0})
}
struct S;
impl S {
m!();
}
fn test() { S.foo()<|>; }
"#,
);
assert_eq!(t, "u128");
}
#[test] #[test]
fn infer_macro_with_dollar_crate_is_correct_in_expr() { fn infer_macro_with_dollar_crate_is_correct_in_expr() {
let (db, pos) = TestDB::with_position( let (db, pos) = TestDB::with_position(