]> git.lizzy.rs Git - rust.git/commitdiff
Add support macros in impl blocks
authorEdwin Cheng <edwin0cheng@gmail.com>
Fri, 20 Dec 2019 19:37:03 +0000 (03:37 +0800)
committerEdwin Cheng <edwin0cheng@gmail.com>
Fri, 20 Dec 2019 19:37:03 +0000 (03:37 +0800)
crates/ra_hir_def/src/body.rs
crates/ra_hir_def/src/data.rs
crates/ra_hir_def/src/lib.rs
crates/ra_hir_expand/src/db.rs
crates/ra_hir_ty/src/tests/macros.rs

index 401fe0b9b55c23813b76fe2fab96f1776313b9d6..a92c01f86f2c8f5f8c16e349c3931d2738abf6c8 100644 (file)
@@ -23,7 +23,7 @@
     DefWithBodyId, HasModule, Lookup, ModuleDefId, ModuleId,
 };
 
-struct Expander {
+pub(crate) struct Expander {
     crate_def_map: Arc<CrateDefMap>,
     current_file_id: HirFileId,
     hygiene: Hygiene,
@@ -32,18 +32,22 @@ struct 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 hygiene = Hygiene::new(db, 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 }
     }
 
-    fn enter_expand(
+    pub(crate) fn enter_expand<T: ast::AstNode, DB: DefDatabase>(
         &mut self,
-        db: &impl DefDatabase,
+        db: &DB,
         macro_call: ast::MacroCall,
-    ) -> Option<(Mark, ast::Expr)> {
+    ) -> Option<(Mark, T)> {
         let ast_id = AstId::new(
             self.current_file_id,
             db.ast_id_map(self.current_file_id).ast_id(&macro_call),
@@ -54,7 +58,7 @@ fn enter_expand(
                 let call_id = def.as_call_id(db, MacroCallKind::FnLike(ast_id));
                 let file_id = call_id.as_file();
                 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());
 
                         let mark = Mark {
@@ -77,14 +81,14 @@ fn enter_expand(
         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.current_file_id = mark.file_id;
         self.ast_id_map = mem::take(&mut mark.ast_id_map);
         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 }
     }
 
@@ -109,7 +113,7 @@ fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> {
     }
 }
 
-struct Mark {
+pub(crate) struct Mark {
     file_id: HirFileId,
     ast_id_map: Arc<AstIdMap>,
     bomb: DropBomb,
index 14e86936bc9da8225154703f0853c488fd2ea9d3..b0a3f178457dc51fdae3e8c261f2a9a02fce4769 100644 (file)
@@ -4,16 +4,16 @@
 
 use hir_expand::{
     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::{
     db::DefDatabase,
     src::HasSource,
     type_ref::{Mutability, TypeRef},
-    AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, ImplId, Intern,
-    Lookup, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
+    AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule,
+    ImplId, Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -167,46 +167,24 @@ pub struct ImplData {
 
 impl ImplData {
     pub(crate) fn impl_data_query(db: &impl DefDatabase, id: ImplId) -> Arc<ImplData> {
-        let src = id.lookup(db).source(db);
-        let items = db.ast_id_map(src.file_id);
+        let impl_loc = id.lookup(db);
+        let src = impl_loc.source(db);
 
         let target_trait = src.value.target_trait().map(TypeRef::from_ast);
         let target_type = TypeRef::from_ast_opt(src.value.target_type());
         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() {
-            item_list
-                .impl_items()
-                .map(|item_node| match item_node {
-                    ast::ImplItem::FnDef(it) => {
-                        let def = FunctionLoc {
-                            container: AssocContainerId::ImplId(id),
-                            ast_id: AstId::new(src.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(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 mut items = Vec::new();
+        if let Some(item_list) = src.value.item_list() {
+            items.extend(collect_impl_items(db, item_list.impl_items(), src.file_id, id));
+            items.extend(collect_impl_items_in_macros(
+                db,
+                module_id,
+                &src.with_value(item_list),
+                id,
+            ));
+        }
 
         let res = ImplData { target_trait, target_type, items, is_negative };
         Arc::new(res)
@@ -237,3 +215,66 @@ fn new<N: NameOwner + TypeAscriptionOwner>(node: &N) -> ConstData {
         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()
+}
index 8ed1599ffb1ea3624b9b776cdad58b0f4b2ba9f7..6bb5408a862fb820dce49341215a52e547e9688e 100644 (file)
@@ -47,6 +47,7 @@
 use ra_db::{impl_intern_key, salsa, CrateId};
 use ra_syntax::{ast, AstNode};
 
+use crate::body::Expander;
 use crate::builtin_type::BuiltinType;
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
index f68aca789834f236e252797e0e6bc2e8f668cd98..d86445abfff6d93efe680f2ea425553222b5281e 100644 (file)
@@ -188,6 +188,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, macro_call_id: MacroCallId) -> Fragmen
         ARG_LIST => FragmentKind::Expr,
         TRY_EXPR => FragmentKind::Expr,
         TUPLE_EXPR => FragmentKind::Expr,
+        ITEM_LIST => FragmentKind::Items,
         _ => {
             // Unknown , Just guess it is `Items`
             FragmentKind::Items
index 812f171db8ed428ccf7a996294cd000b66f7d73e..7fdbf996f42de2bf373d2d2c60252ee2f879efa3 100644 (file)
@@ -182,6 +182,25 @@ trait Trait { fn foo(self) -> 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]
 fn infer_macro_with_dollar_crate_is_correct_in_expr() {
     let (db, pos) = TestDB::with_position(