mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 14:13:58 +00:00
Add support macros in impl blocks
This commit is contained in:
parent
cfc50ff160
commit
ad81d1dbc1
5 changed files with 114 additions and 48 deletions
|
@ -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(¯o_call),
|
db.ast_id_map(self.current_file_id).ast_id(¯o_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,
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue