From 2ae667f4d7d8be72c28be45e989cc5a316ff5b78 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Thu, 22 Aug 2019 01:16:45 +0700 Subject: [PATCH 01/26] Collect derive attributes as a special kind of macro call in raw items --- crates/ra_hir/src/attributes.rs | 70 ++++++++++++++++++++++++++++++ crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/nameres/raw.rs | 32 +++++++++----- crates/ra_syntax/src/ast/traits.rs | 19 ++++++-- 4 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 crates/ra_hir/src/attributes.rs diff --git a/crates/ra_hir/src/attributes.rs b/crates/ra_hir/src/attributes.rs new file mode 100644 index 000000000000..e907ede45251 --- /dev/null +++ b/crates/ra_hir/src/attributes.rs @@ -0,0 +1,70 @@ +use ra_syntax::ast::{TokenTree, Attr}; +use ra_syntax::SmolStr; +use crate::db::{DefDatabase, AstDatabase}; +use std::sync::Arc; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct DeriveData { + traits: Vec, +} + +impl DeriveData { + pub fn new(traits: Vec) -> Self { + DeriveData { traits } + } + + pub fn new_from_tt(tt: TokenTree) -> Self { + // FIXME: collect trait names separated by comma from token tree + Self::new(vec![]) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AttrData { + Derive(DeriveData), + MustUse(Option), + Unknown, +} + +impl AttrData { + pub fn new(attr: AttrKind) -> Self { + match attr { + AttrKind::Derive(tt) => AttrData::Derive(DeriveData::new_from_tt(tt)), + AttrKind::MustUse(s) => AttrData::MustUse(s), + _ => AttrData::Unknown, + } + } +} + +pub enum AttrKind { + Derive(TokenTree), + MustUse(Option), + Other, +} + +impl AttrKind { + pub fn from_attr(attr: Attr) -> Self { + if let Some((kind, tt)) = attr.as_call() { + match kind.as_str() { + "derive" => return AttrKind::Derive(tt), + _ => {}, + } + } + + if let Some(kind) = attr.as_atom() { + match kind.as_str() { + "must_use" => return AttrKind::MustUse(None), + _ => {}, + } + } + + if let Some((kind, value)) = attr.as_key_value() { + match kind.as_str() { + "must_use" => return AttrKind::MustUse(Some(value)), + _ => {} + } + } + + AttrKind::Other + } +} diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index a9de9fb6b1c4..1271c7c0992e 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -49,6 +49,7 @@ mod expr; mod lang_item; mod generics; mod resolve; +mod attributes; pub mod diagnostics; mod code_model; diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 8bf883ac2fe8..8c3fa19e9dbc 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -11,6 +11,7 @@ use crate::{ db::{AstDatabase, DefDatabase}, AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, }; +use ra_syntax::ast::MacroKind; /// `RawItems` is a set of top-level items in a file (except for impls). /// @@ -337,19 +338,26 @@ impl RawItemsCollector { } } - fn add_macro(&mut self, current_module: Option, m: ast::MacroCall) { - let path = match m.path().and_then(Path::from_ast) { - Some(it) => it, - _ => return, - }; - - let name = m.name().map(|it| it.as_name()); - let ast_id = self.source_ast_id_map.ast_id(&m); - let export = m.has_atom_attr("macro_export") - || m.attrs().filter_map(|x| x.as_call()).any(|(name, _)| name == "macro_export"); + fn add_macro(&mut self, current_module: Option, m: MacroKind) { + match m { + MacroKind::Call(m) => { + let path = match m.path().and_then(Path::from_ast) { + Some(it) => it, + _ => return, + }; + + let name = m.name().map(|it| it.as_name()); + let ast_id = self.source_ast_id_map.ast_id(&m); + let export = m.has_atom_attr("macro_export"); + let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); + self.push_item(current_module, RawItem::Macro(m)); + } - let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); - self.push_item(current_module, RawItem::Macro(m)); + MacroKind::DeriveAttr(attr) => { + // TODO + unimplemented!() + } + } } fn push_import( diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index c3e676d4c25d..6b74b1d3f8ab 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs @@ -45,10 +45,16 @@ pub trait FnDefOwner: AstNode { } } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MacroKind { + Call(ast::MacroCall), + DeriveAttr(ast::Attr), +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum ItemOrMacro { Item(ast::ModuleItem), - Macro(ast::MacroCall), + Macro(MacroKind), } pub trait ModuleItemOwner: AstNode { @@ -71,8 +77,15 @@ impl Iterator for ItemOrMacroIter { if let Some(item) = ast::ModuleItem::cast(n.clone()) { return Some(ItemOrMacro::Item(item)); } - if let Some(call) = ast::MacroCall::cast(n) { - return Some(ItemOrMacro::Macro(call)); + if let Some(call) = ast::MacroCall::cast(n.clone()) { + return Some(ItemOrMacro::Macro(MacroKind::Call(call))); + } + if let Some(attr) = ast::Attr::cast(n) { + if let Some((name, _)) = attr.as_call() { + if name.as_str() == "derive" { + return Some(ItemOrMacro::Macro(MacroKind::DeriveAttr(attr))); + } + } } } } From 85ce56a7db23a23f3ddee68f30fe91ab7c8be09f Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Thu, 22 Aug 2019 01:17:33 +0700 Subject: [PATCH 02/26] rustfmt --- crates/ra_hir/src/attributes.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ra_hir/src/attributes.rs b/crates/ra_hir/src/attributes.rs index e907ede45251..1166d6da9f53 100644 --- a/crates/ra_hir/src/attributes.rs +++ b/crates/ra_hir/src/attributes.rs @@ -1,6 +1,6 @@ -use ra_syntax::ast::{TokenTree, Attr}; +use crate::db::{AstDatabase, DefDatabase}; +use ra_syntax::ast::{Attr, TokenTree}; use ra_syntax::SmolStr; -use crate::db::{DefDatabase, AstDatabase}; use std::sync::Arc; #[derive(Debug, Clone, PartialEq, Eq)] @@ -47,14 +47,14 @@ impl AttrKind { if let Some((kind, tt)) = attr.as_call() { match kind.as_str() { "derive" => return AttrKind::Derive(tt), - _ => {}, + _ => {} } } if let Some(kind) = attr.as_atom() { match kind.as_str() { "must_use" => return AttrKind::MustUse(None), - _ => {}, + _ => {} } } From 4d377eba0680dfd37aeb341da8c283e5c2ca5506 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Thu, 22 Aug 2019 22:08:28 +0700 Subject: [PATCH 03/26] Collect derive attributes and modify MacroData to hold derive attribute --- crates/ra_hir/src/nameres/collector.rs | 80 +++++++++++++++----------- crates/ra_hir/src/nameres/raw.rs | 27 +++++++-- crates/ra_hir/src/source_id.rs | 17 +++++- crates/ra_syntax/src/ast/traits.rs | 38 +++++++++--- 4 files changed, 116 insertions(+), 46 deletions(-) diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 6b253ac409e0..589da0b42d9a 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -559,7 +559,7 @@ where raw_items: self.raw_items, parent_module: Some(parent_module), } - .collect(&*items); + .collect(&*items); if *is_macro_use { self.import_all_legacy_macros(module_id); } @@ -588,7 +588,7 @@ where raw_items: &raw_items, parent_module: None, } - .collect(raw_items.items()); + .collect(raw_items.items()); if *is_macro_use { self.import_all_legacy_macros(module_id); } @@ -659,43 +659,55 @@ where } fn collect_macro(&mut self, mac: &raw::MacroData) { - // Case 1: macro rules, define a macro in crate-global mutable scope - if is_macro_rules(&mac.path) { - if let Some(name) = &mac.name { - let macro_id = MacroDefId(mac.ast_id.with_file_id(self.file_id)); - let macro_ = MacroDef { id: macro_id }; - self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export); - } - return; - } + match mac { + raw::MacroData::MacroCall(mac) => { + // Case 1: macro rules, define a macro in crate-global mutable scope + if is_macro_rules(&mac.path) { + if let Some(name) = &mac.name { + let macro_id = MacroDefId(mac.ast_id.with_file_id(self.file_id)); + let macro_ = MacroDef { id: macro_id }; + self.def_collector.define_macro( + self.module_id, + name.clone(), + macro_, + mac.export, + ); + } + return; + } - let ast_id = mac.ast_id.with_file_id(self.file_id); + let ast_id = mac.ast_id.with_file_id(self.file_id); - // Case 2: try to resolve in legacy scope and expand macro_rules, triggering - // recursive item collection. - if let Some(macro_def) = mac.path.as_ident().and_then(|name| { - self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) - }) { - let def = macro_def.id; - let macro_call_id = MacroCallLoc { def, ast_id }.id(self.def_collector.db); + // Case 2: try to resolve in legacy scope and expand macro_rules, triggering + // recursive item collection. + if let Some(macro_def) = mac + .path + .as_ident() + .and_then(|name| { + self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) + }) { + let def = macro_def.id; + let macro_call_id = MacroCallLoc { def, ast_id }.id(self.def_collector.db); + + self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def); + return; + } - self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def); - return; - } + // Case 3: resolve in module scope, expand during name resolution. + // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only. + let mut path = mac.path.clone(); + if path.is_ident() { + path.kind = PathKind::Self_; + } + self.def_collector.unexpanded_macros.push((self.module_id, ast_id, path)); + } - // Case 3: resolve in module scope, expand during name resolution. - // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only. - let mut path = mac.path.clone(); - if path.is_ident() { - path.kind = PathKind::Self_; - } - self.def_collector.unexpanded_macros.push((self.module_id, ast_id, path)); - } + raw::MacroData::DeriveAttr(mac) => { + let ast_id = mac.ast_id.with_file_id(self.file_id); + log::warn!("Derive macro {:?}", ast_id); - fn import_all_legacy_macros(&mut self, module_id: CrateModuleId) { - let macros = self.def_collector.def_map[module_id].scope.legacy_macros.clone(); - for (name, macro_) in macros { - self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_); + // TODO: add trait implementations into the module's scope + } } } } diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 8c3fa19e9dbc..16a0bb04b1f6 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -189,7 +189,18 @@ pub(super) struct Macro(RawId); impl_arena_id!(Macro); #[derive(Debug, PartialEq, Eq)] -pub(super) struct MacroData { +pub(super) enum MacroData { + MacroCall(MacroCallData), + DeriveAttr(DeriveData), +} + +#[derive(Debug, PartialEq, Eq)] +pub(super) struct DeriveData { + pub(super) ast_id: FileAstId, +} + +#[derive(Debug, PartialEq, Eq)] +pub(super) struct MacroCallData { pub(super) ast_id: FileAstId, pub(super) path: Path, pub(super) name: Option, @@ -349,13 +360,21 @@ impl RawItemsCollector { let name = m.name().map(|it| it.as_name()); let ast_id = self.source_ast_id_map.ast_id(&m); let export = m.has_atom_attr("macro_export"); - let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export }); + let m = self.raw_items.macros.alloc(MacroData::MacroCall(MacroCallData { + ast_id, + path, + name, + export, + })); self.push_item(current_module, RawItem::Macro(m)); } MacroKind::DeriveAttr(attr) => { - // TODO - unimplemented!() + log::warn!("Derive attr: {}", attr.syntax().text().to_string()); + let ast_id = self.source_ast_id_map.ast_id(&attr); + let m = self.raw_items.macros.alloc(MacroData::DeriveAttr(DeriveData { ast_id })); + + self.push_item(current_module, RawItem::Macro(m)); } } } diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs index 04574876dac1..442661c4b6ef 100644 --- a/crates/ra_hir/src/source_id.rs +++ b/crates/ra_hir/src/source_id.rs @@ -5,7 +5,10 @@ use std::{ }; use ra_arena::{impl_arena_id, Arena, RawId}; -use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr}; +use ra_syntax::{ + ast::{self, AttrsOwner}, + AstNode, SyntaxNode, SyntaxNodePtr, +}; use crate::{db::AstDatabase, HirFileId}; @@ -132,6 +135,18 @@ impl AstIdMap { // trait does not change ids of top-level items, which helps caching. bfs(node, |it| { if let Some(module_item) = ast::ModuleItem::cast(it.clone()) { + // Collect attributes from `StructDef`s and `EnumDef`s + if let Some(s) = ast::StructDef::cast(it.clone()) { + for attr in s.attrs() { + res.alloc(attr.syntax()); + } + } + if let Some(e) = ast::EnumDef::cast(it.clone()) { + for attr in e.attrs() { + res.alloc(attr.syntax()); + } + } + res.alloc(module_item.syntax()); } else if let Some(macro_call) = ast::MacroCall::cast(it) { res.alloc(macro_call.syntax()); diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index 6b74b1d3f8ab..14e82973fbfb 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs @@ -69,24 +69,48 @@ pub trait ModuleItemOwner: AstNode { #[derive(Debug)] pub struct ItemOrMacroIter(SyntaxNodeChildren); +fn collect_derive_attr(n: impl AttrsOwner) -> Option { + let attrs = n.attrs(); + + for attr in attrs { + if let Some((name, _)) = attr.as_call() { + if name.as_str() == "derive" { + return Some(ItemOrMacro::Macro(MacroKind::DeriveAttr(attr))); + } + } + } + + None +} + impl Iterator for ItemOrMacroIter { type Item = ItemOrMacro; fn next(&mut self) -> Option { loop { let n = self.0.next()?; + if let Some(item) = ast::ModuleItem::cast(n.clone()) { + if let Some(s) = ast::StructDef::cast(n.clone()) { + let m = collect_derive_attr(s); + + if m.is_some() { + return m; + } + } + + if let Some(e) = ast::EnumDef::cast(n.clone()) { + let m = collect_derive_attr(e); + + if m.is_some() { + return m; + } + } + return Some(ItemOrMacro::Item(item)); } if let Some(call) = ast::MacroCall::cast(n.clone()) { return Some(ItemOrMacro::Macro(MacroKind::Call(call))); } - if let Some(attr) = ast::Attr::cast(n) { - if let Some((name, _)) = attr.as_call() { - if name.as_str() == "derive" { - return Some(ItemOrMacro::Macro(MacroKind::DeriveAttr(attr))); - } - } - } } } } From 75ab5da2cc62bb7af6a90f41b0b0c44ba58629ba Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Thu, 22 Aug 2019 22:55:43 +0700 Subject: [PATCH 04/26] Make AstId public globally --- crates/ra_hir/src/source_id.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_hir/src/source_id.rs b/crates/ra_hir/src/source_id.rs index 442661c4b6ef..a809d5554a5c 100644 --- a/crates/ra_hir/src/source_id.rs +++ b/crates/ra_hir/src/source_id.rs @@ -16,7 +16,7 @@ use crate::{db::AstDatabase, HirFileId}; /// /// It is stable across reparses, and can be used as salsa key/value. #[derive(Debug)] -pub(crate) struct AstId { +pub struct AstId { file_id: HirFileId, file_ast_id: FileAstId, } From eacaeb1387941bf918973710f58fbefd57ebdb38 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Thu, 22 Aug 2019 23:03:19 +0700 Subject: [PATCH 05/26] Extend MacroCallId and MacroCallLoc to be aware of Derive macros --- crates/ra_hir/src/ids.rs | 136 ++++++++++++++++++------- crates/ra_hir/src/nameres/collector.rs | 18 +++- 2 files changed, 111 insertions(+), 43 deletions(-) diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 9ea4e695d969..35996078b437 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -43,7 +43,10 @@ impl HirFileId { HirFileIdRepr::File(file_id) => file_id, HirFileIdRepr::Macro(macro_file) => { let loc = macro_file.macro_call_id.loc(db); - loc.ast_id.file_id().original_file(db) + match loc { + MacroCallLoc::Macro { ast_id, .. } => ast_id.file_id().original_file(db), + MacroCallLoc::Derive { ast_id, .. } => ast_id.file_id().original_file(db), + } } } } @@ -139,10 +142,17 @@ pub(crate) fn macro_def_query(db: &impl AstDatabase, id: MacroDefId) -> Option Option> { let loc = id.loc(db); - let macro_call = loc.ast_id.to_node(db); - let arg = macro_call.token_tree()?; - let (tt, _) = mbe::ast_to_token_tree(&arg)?; - Some(Arc::new(tt)) + + match loc { + MacroCallLoc::Macro { ast_id, .. } => { + let macro_call = ast_id.to_node(db); + let arg = macro_call.token_tree()?; + let (tt, _) = mbe::ast_to_token_tree(&arg)?; + Some(Arc::new(tt)) + } + + MacroCallLoc::Derive { .. } => None + } } pub(crate) fn macro_expand_query( @@ -150,16 +160,30 @@ pub(crate) fn macro_expand_query( id: MacroCallId, ) -> Result, String> { let loc = id.loc(db); - let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; - let macro_rules = db.macro_def(loc.def).ok_or("Fail to find macro definition")?; - let tt = macro_rules.expand(¯o_arg).map_err(|err| format!("{:?}", err))?; - // Set a hard limit for the expanded tt - let count = tt.count(); - if count > 65536 { - return Err(format!("Total tokens count exceed limit : count = {}", count)); + match loc { + MacroCallLoc::Macro { ast_id, def } => { + let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; + + let macro_rules = db.macro_def(def).ok_or("Fail to find macro definition")?; + let tt = macro_rules.expand(¯o_arg).map_err(|err| format!("{:?}", err))?; + // Set a hard limit for the expanded tt + let count = tt.count(); + if count > 65536 { + return Err(format!("Total tokens count exceed limit : count = {}", count)); + } + Ok(Arc::new(tt)) + }, + + MacroCallLoc::Derive { ast_id} => { + // TODO: produce token trees of derive macro expansion + + Ok(Arc::new(tt::Subtree { + delimiter: tt::Delimiter::None, + token_trees: vec![] + })) + }, } - Ok(Arc::new(tt)) } macro_rules! impl_intern_key { @@ -182,9 +206,15 @@ pub struct MacroCallId(salsa::InternId); impl_intern_key!(MacroCallId); #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct MacroCallLoc { - pub(crate) def: MacroDefId, - pub(crate) ast_id: AstId, +pub enum MacroCallLoc { + Macro { + def: MacroDefId, + ast_id: AstId, + }, + + Derive { + ast_id: AstId, + } } impl MacroCallId { @@ -364,29 +394,57 @@ impl AstItemDef for TypeAliasId { impl MacroCallId { pub fn debug_dump(self, db: &impl AstDatabase) -> String { let loc = self.loc(db); - let node = loc.ast_id.to_node(db); - let syntax_str = { - let mut res = String::new(); - node.syntax().text().for_each_chunk(|chunk| { - if !res.is_empty() { - res.push(' ') - } - res.push_str(chunk) - }); - res - }; - - // dump the file name - let file_id: HirFileId = self.loc(db).ast_id.file_id(); - let original = file_id.original_file(db); - let macro_rules = db.macro_def(loc.def); - - format!( - "macro call [file: {:?}] : {}\nhas rules: {}", - db.file_relative_path(original), - syntax_str, - macro_rules.is_some() - ) + match loc { + MacroCallLoc::Macro { ast_id, def } => { + let node = ast_id.to_node(db); + let syntax_str = { + let mut res = String::new(); + node.syntax().text().for_each_chunk(|chunk| { + if !res.is_empty() { + res.push(' ') + } + res.push_str(chunk) + }); + res + }; + + // dump the file name + let file_id: HirFileId = ast_id.file_id(); + let original = file_id.original_file(db); + let macro_rules = db.macro_def(def); + + format!( + "macro call [file: {:?}] : {}\nhas rules: {}", + db.file_relative_path(original), + syntax_str, + macro_rules.is_some() + ) + } + + MacroCallLoc::Derive { ast_id } => { + let node = ast_id.to_node(db); + let syntax_str = { + let mut res = String::new(); + node.syntax().text().for_each_chunk(|chunk| { + if !res.is_empty() { + res.push(' ') + } + res.push_str(chunk) + }); + res + }; + + // dump the file name + let file_id: HirFileId = ast_id.file_id(); + let original = file_id.original_file(db); + + format!( + "derive macro [file: {:?}] : {}", + db.file_relative_path(original), + syntax_str, + ) + } + } } } diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 589da0b42d9a..a186ed9062ef 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -439,7 +439,7 @@ where ); if let Some(def) = resolved_res.resolved_def.get_macros() { - let call_id = MacroCallLoc { def: def.id, ast_id: *ast_id }.id(self.db); + let call_id = MacroCallLoc::Macro { def: def.id, ast_id: *ast_id }.id(self.db); resolved.push((*module_id, call_id, def.id)); res = ReachedFixedPoint::No; return false; @@ -687,7 +687,7 @@ where self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) }) { let def = macro_def.id; - let macro_call_id = MacroCallLoc { def, ast_id }.id(self.def_collector.db); + let macro_call_id = MacroCallLoc::Macro { def, ast_id }.id(self.def_collector.db); self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def); return; @@ -704,9 +704,19 @@ where raw::MacroData::DeriveAttr(mac) => { let ast_id = mac.ast_id.with_file_id(self.file_id); - log::warn!("Derive macro {:?}", ast_id); + let macro_call_id = MacroCallLoc::Derive { ast_id }.id(self.def_collector.db); - // TODO: add trait implementations into the module's scope + // Collect from derive macro expansion + let file_id: HirFileId = macro_call_id.as_file(MacroFileKind::Items); + let raw_items = self.def_collector.db.raw_items(file_id); + ModCollector { + def_collector: &mut *self.def_collector, + file_id, + module_id: self.module_id, + raw_items: &raw_items, + parent_module: None, + } + .collect(raw_items.items()); } } } From b4dd7414f5247f62bfd4e18d134926406441c64d Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Thu, 22 Aug 2019 23:10:59 +0700 Subject: [PATCH 06/26] rustfmt --- crates/ra_hir/src/ids.rs | 24 ++++++++---------------- crates/ra_hir/src/nameres/collector.rs | 5 +++-- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 35996078b437..855db86aad64 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -44,7 +44,7 @@ impl HirFileId { HirFileIdRepr::Macro(macro_file) => { let loc = macro_file.macro_call_id.loc(db); match loc { - MacroCallLoc::Macro { ast_id, .. } => ast_id.file_id().original_file(db), + MacroCallLoc::Macro { ast_id, .. } => ast_id.file_id().original_file(db), MacroCallLoc::Derive { ast_id, .. } => ast_id.file_id().original_file(db), } } @@ -151,7 +151,7 @@ pub(crate) fn macro_arg_query(db: &impl AstDatabase, id: MacroCallId) -> Option< Some(Arc::new(tt)) } - MacroCallLoc::Derive { .. } => None + MacroCallLoc::Derive { .. } => None, } } @@ -173,16 +173,13 @@ pub(crate) fn macro_expand_query( return Err(format!("Total tokens count exceed limit : count = {}", count)); } Ok(Arc::new(tt)) - }, + } - MacroCallLoc::Derive { ast_id} => { + MacroCallLoc::Derive { ast_id } => { // TODO: produce token trees of derive macro expansion - Ok(Arc::new(tt::Subtree { - delimiter: tt::Delimiter::None, - token_trees: vec![] - })) - }, + Ok(Arc::new(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: vec![] })) + } } } @@ -207,14 +204,9 @@ impl_intern_key!(MacroCallId); #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum MacroCallLoc { - Macro { - def: MacroDefId, - ast_id: AstId, - }, + Macro { def: MacroDefId, ast_id: AstId }, - Derive { - ast_id: AstId, - } + Derive { ast_id: AstId }, } impl MacroCallId { diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index a186ed9062ef..ddb3358470e6 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -687,7 +687,8 @@ where self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) }) { let def = macro_def.id; - let macro_call_id = MacroCallLoc::Macro { def, ast_id }.id(self.def_collector.db); + let macro_call_id = + MacroCallLoc::Macro { def, ast_id }.id(self.def_collector.db); self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def); return; @@ -716,7 +717,7 @@ where raw_items: &raw_items, parent_module: None, } - .collect(raw_items.items()); + .collect(raw_items.items()); } } } From dfdb5f0b053432a6bc2bd31d186a42c9d12dfe19 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sat, 31 Aug 2019 12:50:51 +0700 Subject: [PATCH 07/26] Add target to attribute macro and generalize attribute macros --- crates/ra_hir/src/ids.rs | 35 ++++++++++------------- crates/ra_hir/src/nameres/collector.rs | 11 +++++--- crates/ra_hir/src/nameres/raw.rs | 19 ++++++++----- crates/ra_syntax/src/ast/traits.rs | 39 +++++++++++--------------- 4 files changed, 51 insertions(+), 53 deletions(-) diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 855db86aad64..f9b58f0535f6 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -45,7 +45,9 @@ impl HirFileId { let loc = macro_file.macro_call_id.loc(db); match loc { MacroCallLoc::Macro { ast_id, .. } => ast_id.file_id().original_file(db), - MacroCallLoc::Derive { ast_id, .. } => ast_id.file_id().original_file(db), + MacroCallLoc::Attribute { attr_ast_id, .. } => { + attr_ast_id.file_id().original_file(db) + } } } } @@ -151,7 +153,7 @@ pub(crate) fn macro_arg_query(db: &impl AstDatabase, id: MacroCallId) -> Option< Some(Arc::new(tt)) } - MacroCallLoc::Derive { .. } => None, + MacroCallLoc::Attribute { .. } => None, } } @@ -162,7 +164,7 @@ pub(crate) fn macro_expand_query( let loc = id.loc(db); match loc { - MacroCallLoc::Macro { ast_id, def } => { + MacroCallLoc::Macro { ast_id: _, def } => { let macro_arg = db.macro_arg(id).ok_or("Fail to args in to tt::TokenTree")?; let macro_rules = db.macro_def(def).ok_or("Fail to find macro definition")?; @@ -175,7 +177,8 @@ pub(crate) fn macro_expand_query( Ok(Arc::new(tt)) } - MacroCallLoc::Derive { ast_id } => { + MacroCallLoc::Attribute { attr_ast_id: _, .. } => { + log::warn!("Expanding: {}", id.debug_dump(db)); // TODO: produce token trees of derive macro expansion Ok(Arc::new(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: vec![] })) @@ -206,7 +209,7 @@ impl_intern_key!(MacroCallId); pub enum MacroCallLoc { Macro { def: MacroDefId, ast_id: AstId }, - Derive { ast_id: AstId }, + Attribute { attr_ast_id: AstId, target_ast_id: AstId }, } impl MacroCallId { @@ -413,27 +416,19 @@ impl MacroCallId { ) } - MacroCallLoc::Derive { ast_id } => { - let node = ast_id.to_node(db); - let syntax_str = { - let mut res = String::new(); - node.syntax().text().for_each_chunk(|chunk| { - if !res.is_empty() { - res.push(' ') - } - res.push_str(chunk) - }); - res - }; + MacroCallLoc::Attribute { attr_ast_id, target_ast_id } => { + let attr_node = attr_ast_id.to_node(db); + let target_node = target_ast_id.to_node(db); // dump the file name - let file_id: HirFileId = ast_id.file_id(); + let file_id: HirFileId = attr_ast_id.file_id(); let original = file_id.original_file(db); format!( - "derive macro [file: {:?}] : {}", + "attribute macro [file: {:?}] : {} applied to {}", db.file_relative_path(original), - syntax_str, + attr_node.syntax().to_string(), + target_node.syntax().to_string(), ) } } diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index ddb3358470e6..002087e85e90 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -703,11 +703,14 @@ where self.def_collector.unexpanded_macros.push((self.module_id, ast_id, path)); } - raw::MacroData::DeriveAttr(mac) => { - let ast_id = mac.ast_id.with_file_id(self.file_id); - let macro_call_id = MacroCallLoc::Derive { ast_id }.id(self.def_collector.db); + raw::MacroData::Attr(mac) => { + let attr_ast_id = mac.attr_ast_id.with_file_id(self.file_id); + let target_ast_id = mac.target_ast_id.with_file_id(self.file_id); + + let macro_call_id = MacroCallLoc::Attribute { attr_ast_id, target_ast_id } + .id(self.def_collector.db); - // Collect from derive macro expansion + // Collect items from attribute macro expansion let file_id: HirFileId = macro_call_id.as_file(MacroFileKind::Items); let raw_items = self.def_collector.db.raw_items(file_id); ModCollector { diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 16a0bb04b1f6..8f38e50f0d9a 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -191,12 +191,13 @@ impl_arena_id!(Macro); #[derive(Debug, PartialEq, Eq)] pub(super) enum MacroData { MacroCall(MacroCallData), - DeriveAttr(DeriveData), + Attr(AttrData), } #[derive(Debug, PartialEq, Eq)] -pub(super) struct DeriveData { - pub(super) ast_id: FileAstId, +pub(super) struct AttrData { + pub(super) attr_ast_id: FileAstId, + pub(super) target_ast_id: FileAstId, } #[derive(Debug, PartialEq, Eq)] @@ -369,10 +370,14 @@ impl RawItemsCollector { self.push_item(current_module, RawItem::Macro(m)); } - MacroKind::DeriveAttr(attr) => { - log::warn!("Derive attr: {}", attr.syntax().text().to_string()); - let ast_id = self.source_ast_id_map.ast_id(&attr); - let m = self.raw_items.macros.alloc(MacroData::DeriveAttr(DeriveData { ast_id })); + MacroKind::Attr { attr, target } => { + let attr_ast_id = self.source_ast_id_map.ast_id(&attr); + let target_ast_id = self.source_ast_id_map.ast_id(&target); + + let m = self + .raw_items + .macros + .alloc(MacroData::Attr(AttrData { attr_ast_id, target_ast_id })); self.push_item(current_module, RawItem::Macro(m)); } diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index 14e82973fbfb..105087bac0c6 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs @@ -48,7 +48,7 @@ pub trait FnDefOwner: AstNode { #[derive(Debug, Clone, PartialEq, Eq)] pub enum MacroKind { Call(ast::MacroCall), - DeriveAttr(ast::Attr), + Attr { attr: ast::Attr, target: ast::ModuleItem }, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -69,14 +69,21 @@ pub trait ModuleItemOwner: AstNode { #[derive(Debug)] pub struct ItemOrMacroIter(SyntaxNodeChildren); -fn collect_derive_attr(n: impl AttrsOwner) -> Option { - let attrs = n.attrs(); +fn collect_derive_attr(item: ast::ModuleItem) -> Option { + let item_node = item.syntax(); + let mut attrs = None; - for attr in attrs { - if let Some((name, _)) = attr.as_call() { - if name.as_str() == "derive" { - return Some(ItemOrMacro::Macro(MacroKind::DeriveAttr(attr))); - } + if let Some(s) = ast::StructDef::cast(item_node.clone()) { + attrs = Some(s.attrs()); + } + + if let Some(e) = ast::EnumDef::cast(item_node.clone()) { + attrs = Some(e.attrs()); + } + + for attr in attrs.into_iter().flatten() { + if attr.as_call().is_some() { + return Some(ItemOrMacro::Macro(MacroKind::Attr { attr, target: item.clone() })); } } @@ -90,20 +97,8 @@ impl Iterator for ItemOrMacroIter { let n = self.0.next()?; if let Some(item) = ast::ModuleItem::cast(n.clone()) { - if let Some(s) = ast::StructDef::cast(n.clone()) { - let m = collect_derive_attr(s); - - if m.is_some() { - return m; - } - } - - if let Some(e) = ast::EnumDef::cast(n.clone()) { - let m = collect_derive_attr(e); - - if m.is_some() { - return m; - } + if let Some(item) = collect_derive_attr(item.clone()) { + return Some(item); } return Some(ItemOrMacro::Item(item)); From 9b01cafd6f6ee04caa64691e6ee6f25510e9730e Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sat, 31 Aug 2019 12:51:03 +0700 Subject: [PATCH 08/26] Remove hir::attributes module --- crates/ra_hir/src/attributes.rs | 70 --------------------------------- crates/ra_hir/src/lib.rs | 1 - 2 files changed, 71 deletions(-) delete mode 100644 crates/ra_hir/src/attributes.rs diff --git a/crates/ra_hir/src/attributes.rs b/crates/ra_hir/src/attributes.rs deleted file mode 100644 index 1166d6da9f53..000000000000 --- a/crates/ra_hir/src/attributes.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::db::{AstDatabase, DefDatabase}; -use ra_syntax::ast::{Attr, TokenTree}; -use ra_syntax::SmolStr; -use std::sync::Arc; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct DeriveData { - traits: Vec, -} - -impl DeriveData { - pub fn new(traits: Vec) -> Self { - DeriveData { traits } - } - - pub fn new_from_tt(tt: TokenTree) -> Self { - // FIXME: collect trait names separated by comma from token tree - Self::new(vec![]) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum AttrData { - Derive(DeriveData), - MustUse(Option), - Unknown, -} - -impl AttrData { - pub fn new(attr: AttrKind) -> Self { - match attr { - AttrKind::Derive(tt) => AttrData::Derive(DeriveData::new_from_tt(tt)), - AttrKind::MustUse(s) => AttrData::MustUse(s), - _ => AttrData::Unknown, - } - } -} - -pub enum AttrKind { - Derive(TokenTree), - MustUse(Option), - Other, -} - -impl AttrKind { - pub fn from_attr(attr: Attr) -> Self { - if let Some((kind, tt)) = attr.as_call() { - match kind.as_str() { - "derive" => return AttrKind::Derive(tt), - _ => {} - } - } - - if let Some(kind) = attr.as_atom() { - match kind.as_str() { - "must_use" => return AttrKind::MustUse(None), - _ => {} - } - } - - if let Some((kind, value)) = attr.as_key_value() { - match kind.as_str() { - "must_use" => return AttrKind::MustUse(Some(value)), - _ => {} - } - } - - AttrKind::Other - } -} diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 1271c7c0992e..a9de9fb6b1c4 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -49,7 +49,6 @@ mod expr; mod lang_item; mod generics; mod resolve; -mod attributes; pub mod diagnostics; mod code_model; From d2dea44ef48d57ae5ca6fdd3c4b1c811afd27c8b Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Wed, 4 Sep 2019 10:56:04 +0300 Subject: [PATCH 09/26] Extract and make text_to_tokentree function public --- crates/ra_mbe/src/lib.rs | 1 + crates/ra_mbe/src/syntax_bridge.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index a0904323cbb4..12dbca0e7eea 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs @@ -33,6 +33,7 @@ pub enum ExpandError { pub use crate::syntax_bridge::{ ast_to_token_tree, syntax_node_to_token_tree, token_tree_to_expr, token_tree_to_items, token_tree_to_macro_stmts, token_tree_to_pat, token_tree_to_ty, + text_to_tokentree, }; /// This struct contains AST for a single `macro_rules` definition. What might diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index 2d035307b178..bb52299860de 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs @@ -18,6 +18,18 @@ pub struct TokenMap { tokens: Vec, } +/// Parses arbitrary language string into AST and returns corresponding `tt::Subtree`. +pub fn text_to_tokentree(text: &str) -> tt::Subtree { + // wrap the given text to a macro call + let wrapped = format!("wrap_macro!( {} )", text); + let wrapped = ast::SourceFile::parse(&wrapped); + let wrapped = wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); + let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0; + wrapped.delimiter = tt::Delimiter::None; + + wrapped +} + /// Convert the syntax tree (what user has written) to a `TokenTree` (what macro /// will consume). pub fn ast_to_token_tree(ast: &ast::TokenTree) -> Option<(tt::Subtree, TokenMap)> { From 2cf123ea5f055f48af0c9fb0e45456429ee6c3d8 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Wed, 4 Sep 2019 11:24:51 +0300 Subject: [PATCH 10/26] Add attr macro expansion modules --- crates/ra_hir/src/attr_macros.rs | 13 +++++++++ crates/ra_hir/src/attr_macros/derive.rs | 27 +++++++++++++++++++ crates/ra_hir/src/attr_macros/tests/derive.rs | 0 crates/ra_hir/src/ids.rs | 16 +++++++---- crates/ra_hir/src/lib.rs | 1 + 5 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 crates/ra_hir/src/attr_macros.rs create mode 100644 crates/ra_hir/src/attr_macros/derive.rs create mode 100644 crates/ra_hir/src/attr_macros/tests/derive.rs diff --git a/crates/ra_hir/src/attr_macros.rs b/crates/ra_hir/src/attr_macros.rs new file mode 100644 index 000000000000..a038ace6bda2 --- /dev/null +++ b/crates/ra_hir/src/attr_macros.rs @@ -0,0 +1,13 @@ +use ra_syntax::ast::{self, AstNode}; + +mod derive; + +pub fn expand_attr_macro(attr_node: ast::Attr, target_node: ast::ModuleItem) -> Option { + if !derive::is_derive_attr(&attr_node) { + log::warn!("Unimplemented macro attr: {}", attr_node.syntax().to_string()); + + return None; + } else { + derive::expand_derive_attr(attr_node, target_node) + } +} \ No newline at end of file diff --git a/crates/ra_hir/src/attr_macros/derive.rs b/crates/ra_hir/src/attr_macros/derive.rs new file mode 100644 index 000000000000..d29818c5a35f --- /dev/null +++ b/crates/ra_hir/src/attr_macros/derive.rs @@ -0,0 +1,27 @@ +use ra_syntax::{ast, AstNode, SmolStr}; + +/// Checks whether `#[attr]` is in the `#[derive()]` form. +pub(crate) fn is_derive_attr(attr_node: &ast::Attr) -> bool { + if let Some((name, _args)) = attr_node.as_call() { + // TODO: check for empty _args tree + name == "derive" + } else { + false + } +} + +pub(crate) fn expand_derive_attr(attr_node: ast::Attr, target_node: ast::ModuleItem) -> Option { + let traits_to_derive = collect_trait_names(&attr_node); + + // TODO: + None +} + +// TODO: try iterate without allocation +fn collect_trait_names(attr_node: &ast::Attr) -> Option> { + let items = attr_node.syntax().children().map(|c| c.to_string()).collect::>(); + log::warn!("Trait names: {:?}", items); + + // TODO: + None +} \ No newline at end of file diff --git a/crates/ra_hir/src/attr_macros/tests/derive.rs b/crates/ra_hir/src/attr_macros/tests/derive.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index f9b58f0535f6..562ba3504e45 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -177,11 +177,17 @@ pub(crate) fn macro_expand_query( Ok(Arc::new(tt)) } - MacroCallLoc::Attribute { attr_ast_id: _, .. } => { - log::warn!("Expanding: {}", id.debug_dump(db)); - // TODO: produce token trees of derive macro expansion - - Ok(Arc::new(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: vec![] })) + MacroCallLoc::Attribute { attr_ast_id, target_ast_id } => { + let attr_node = attr_ast_id.to_node(db); + let target_node = target_ast_id.to_node(db); + + let tt = attr_macros::expand_attr_macro(attr_node, target_node); + if let Some(tt) = tt { + Ok(Arc::new(tt)) + } else { + // Return empty token tree + Ok(Arc::new(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: vec![] })) + } } } } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index a9de9fb6b1c4..1e1699cc42bc 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -49,6 +49,7 @@ mod expr; mod lang_item; mod generics; mod resolve; +mod attr_macros; pub mod diagnostics; mod code_model; From a75d6b482929073fc6ac4bfc93e12013d3463419 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Fri, 6 Sep 2019 23:59:31 +0800 Subject: [PATCH 11/26] Collect names of traits to derive, scaffold for trait implementations --- crates/ra_hir/src/attr_macros/derive.rs | 71 ++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/crates/ra_hir/src/attr_macros/derive.rs b/crates/ra_hir/src/attr_macros/derive.rs index d29818c5a35f..528183bb44cd 100644 --- a/crates/ra_hir/src/attr_macros/derive.rs +++ b/crates/ra_hir/src/attr_macros/derive.rs @@ -1,4 +1,4 @@ -use ra_syntax::{ast, AstNode, SmolStr}; +use ra_syntax::{ast::{self, NameOwner}, AstNode, SyntaxKind}; /// Checks whether `#[attr]` is in the `#[derive()]` form. pub(crate) fn is_derive_attr(attr_node: &ast::Attr) -> bool { @@ -13,15 +13,74 @@ pub(crate) fn is_derive_attr(attr_node: &ast::Attr) -> bool { pub(crate) fn expand_derive_attr(attr_node: ast::Attr, target_node: ast::ModuleItem) -> Option { let traits_to_derive = collect_trait_names(&attr_node); - // TODO: + let tts = traits_to_derive + .into_iter() + .flatten() + .map(|tr| { + match tr.as_str() { + "Debug" => expand_debug(&target_node), + "Copy" => expand_copy(&target_node), + "Clone" => expand_clone(&target_node), + + _ => { + log::warn!("Unimplemented {} trait derive macro attribute", tr); + None + } + } + }) + .flatten() + .collect::>(); + + log::warn!("Token trees after expansion: {:#?}", tts); + None } // TODO: try iterate without allocation -fn collect_trait_names(attr_node: &ast::Attr) -> Option> { - let items = attr_node.syntax().children().map(|c| c.to_string()).collect::>(); - log::warn!("Trait names: {:?}", items); +fn collect_trait_names(attr_node: &ast::Attr) -> Option> { + if let Some((_, tt)) = attr_node.as_call() { + let items = tt.syntax().children_with_tokens() + .into_iter() + .filter(|token| token.kind() == SyntaxKind::IDENT) + .map(|c| c.to_string()) + .collect::>(); + + Some(items) + } else { + None + } +} + +fn item_name(item: &ast::ModuleItem) -> Option { + if let Some(s) = ast::StructDef::cast(item.syntax().clone()) { + s.name().and_then(|n| Some(n.text().to_string())) + } else if let Some(e) = ast::EnumDef::cast(item.syntax().clone()) { + e.name().and_then(|n| Some(n.text().to_string())) + } else { + None + } +} + +fn expand_debug(target: &ast::ModuleItem) -> Option { + if let Some(name) = item_name(target) { + log::warn!("Implementing Debug for {}", name); + } + + None +} + +fn expand_copy(target: &ast::ModuleItem) -> Option { + if let Some(name) = item_name(target) { + log::warn!("Implementing Copy for {}", name); + } + + None +} + +fn expand_clone(target: &ast::ModuleItem) -> Option { + if let Some(name) = item_name(target) { + log::warn!("Implementing Clone for {}", name); + } - // TODO: None } \ No newline at end of file From fe487f90a6f6c76bf9f4e85089911020ea12257b Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sat, 7 Sep 2019 00:04:16 +0800 Subject: [PATCH 12/26] Use explicit module name for ast::MacroKind --- crates/ra_hir/src/nameres/raw.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 8f38e50f0d9a..08e169b068b5 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -11,7 +11,6 @@ use crate::{ db::{AstDatabase, DefDatabase}, AsName, AstIdMap, Either, FileAstId, HirFileId, ModuleSource, Name, Path, }; -use ra_syntax::ast::MacroKind; /// `RawItems` is a set of top-level items in a file (except for impls). /// @@ -350,9 +349,9 @@ impl RawItemsCollector { } } - fn add_macro(&mut self, current_module: Option, m: MacroKind) { + fn add_macro(&mut self, current_module: Option, m: ast::MacroKind) { match m { - MacroKind::Call(m) => { + ast::MacroKind::Call(m) => { let path = match m.path().and_then(Path::from_ast) { Some(it) => it, _ => return, @@ -370,7 +369,7 @@ impl RawItemsCollector { self.push_item(current_module, RawItem::Macro(m)); } - MacroKind::Attr { attr, target } => { + ast::MacroKind::Attr { attr, target } => { let attr_ast_id = self.source_ast_id_map.ast_id(&attr); let target_ast_id = self.source_ast_id_map.ast_id(&target); From ca9f8d3faa7651a615690ab86ff0110d2c0b943c Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sun, 8 Sep 2019 00:48:31 +0800 Subject: [PATCH 13/26] Invert the condition in expand_attr_macro --- crates/ra_hir/src/attr_macros.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/ra_hir/src/attr_macros.rs b/crates/ra_hir/src/attr_macros.rs index a038ace6bda2..6e4714e31c02 100644 --- a/crates/ra_hir/src/attr_macros.rs +++ b/crates/ra_hir/src/attr_macros.rs @@ -3,11 +3,10 @@ use ra_syntax::ast::{self, AstNode}; mod derive; pub fn expand_attr_macro(attr_node: ast::Attr, target_node: ast::ModuleItem) -> Option { - if !derive::is_derive_attr(&attr_node) { - log::warn!("Unimplemented macro attr: {}", attr_node.syntax().to_string()); - - return None; - } else { - derive::expand_derive_attr(attr_node, target_node) + if derive::is_derive_attr(&attr_node) { + return derive::expand_derive_attr(attr_node, target_node); } -} \ No newline at end of file + + log::warn!("Unimplemented macro attr: {}", attr_node.syntax().to_string()); + None +} From a41697278c67abcc60ef1c14389ea826eaff3783 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sun, 8 Sep 2019 00:51:42 +0800 Subject: [PATCH 14/26] rustfmt --- crates/ra_hir/src/attr_macros.rs | 5 +++- crates/ra_hir/src/attr_macros/derive.rs | 32 +++++++++++++++---------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/crates/ra_hir/src/attr_macros.rs b/crates/ra_hir/src/attr_macros.rs index 6e4714e31c02..8ddbb75d25cd 100644 --- a/crates/ra_hir/src/attr_macros.rs +++ b/crates/ra_hir/src/attr_macros.rs @@ -2,7 +2,10 @@ use ra_syntax::ast::{self, AstNode}; mod derive; -pub fn expand_attr_macro(attr_node: ast::Attr, target_node: ast::ModuleItem) -> Option { +pub fn expand_attr_macro( + attr_node: ast::Attr, + target_node: ast::ModuleItem, +) -> Option { if derive::is_derive_attr(&attr_node) { return derive::expand_derive_attr(attr_node, target_node); } diff --git a/crates/ra_hir/src/attr_macros/derive.rs b/crates/ra_hir/src/attr_macros/derive.rs index 528183bb44cd..49b9a3145ebf 100644 --- a/crates/ra_hir/src/attr_macros/derive.rs +++ b/crates/ra_hir/src/attr_macros/derive.rs @@ -1,4 +1,7 @@ -use ra_syntax::{ast::{self, NameOwner}, AstNode, SyntaxKind}; +use ra_syntax::{ + ast::{self, NameOwner}, + AstNode, SyntaxKind, +}; /// Checks whether `#[attr]` is in the `#[derive()]` form. pub(crate) fn is_derive_attr(attr_node: &ast::Attr) -> bool { @@ -10,22 +13,23 @@ pub(crate) fn is_derive_attr(attr_node: &ast::Attr) -> bool { } } -pub(crate) fn expand_derive_attr(attr_node: ast::Attr, target_node: ast::ModuleItem) -> Option { +pub(crate) fn expand_derive_attr( + attr_node: ast::Attr, + target_node: ast::ModuleItem, +) -> Option { let traits_to_derive = collect_trait_names(&attr_node); let tts = traits_to_derive .into_iter() .flatten() - .map(|tr| { - match tr.as_str() { - "Debug" => expand_debug(&target_node), - "Copy" => expand_copy(&target_node), - "Clone" => expand_clone(&target_node), + .map(|tr| match tr.as_str() { + "Debug" => expand_debug(&target_node), + "Copy" => expand_copy(&target_node), + "Clone" => expand_clone(&target_node), - _ => { - log::warn!("Unimplemented {} trait derive macro attribute", tr); - None - } + _ => { + log::warn!("Unimplemented {} trait derive macro attribute", tr); + None } }) .flatten() @@ -39,7 +43,9 @@ pub(crate) fn expand_derive_attr(attr_node: ast::Attr, target_node: ast::ModuleI // TODO: try iterate without allocation fn collect_trait_names(attr_node: &ast::Attr) -> Option> { if let Some((_, tt)) = attr_node.as_call() { - let items = tt.syntax().children_with_tokens() + let items = tt + .syntax() + .children_with_tokens() .into_iter() .filter(|token| token.kind() == SyntaxKind::IDENT) .map(|c| c.to_string()) @@ -83,4 +89,4 @@ fn expand_clone(target: &ast::ModuleItem) -> Option { } None -} \ No newline at end of file +} From 7c5ecfdc724be6212e4978f7adf55fd8e6ce2ea1 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sun, 8 Sep 2019 03:11:41 +0800 Subject: [PATCH 15/26] Add tests, simple empty implementation of derived traits --- crates/ra_hir/src/attr_macros.rs | 5 +- crates/ra_hir/src/attr_macros/derive.rs | 43 ++++-------- crates/ra_hir/src/attr_macros/tests.rs | 1 + crates/ra_hir/src/attr_macros/tests/derive.rs | 65 +++++++++++++++++++ 4 files changed, 82 insertions(+), 32 deletions(-) create mode 100644 crates/ra_hir/src/attr_macros/tests.rs diff --git a/crates/ra_hir/src/attr_macros.rs b/crates/ra_hir/src/attr_macros.rs index 8ddbb75d25cd..aa5c781f9a4f 100644 --- a/crates/ra_hir/src/attr_macros.rs +++ b/crates/ra_hir/src/attr_macros.rs @@ -1,5 +1,8 @@ use ra_syntax::ast::{self, AstNode}; +#[cfg(test)] +mod tests; + mod derive; pub fn expand_attr_macro( @@ -10,6 +13,6 @@ pub fn expand_attr_macro( return derive::expand_derive_attr(attr_node, target_node); } - log::warn!("Unimplemented macro attr: {}", attr_node.syntax().to_string()); + log::debug!("Unimplemented macro attr: {}", attr_node.syntax().to_string()); None } diff --git a/crates/ra_hir/src/attr_macros/derive.rs b/crates/ra_hir/src/attr_macros/derive.rs index 49b9a3145ebf..5ca0484ce1c0 100644 --- a/crates/ra_hir/src/attr_macros/derive.rs +++ b/crates/ra_hir/src/attr_macros/derive.rs @@ -22,25 +22,19 @@ pub(crate) fn expand_derive_attr( let tts = traits_to_derive .into_iter() .flatten() - .map(|tr| match tr.as_str() { - "Debug" => expand_debug(&target_node), - "Copy" => expand_copy(&target_node), - "Clone" => expand_clone(&target_node), - - _ => { - log::warn!("Unimplemented {} trait derive macro attribute", tr); - None - } - }) + .map(|tr| implement_trait_simple(&tr, &target_node)) .flatten() + .map(|subtree| tt::TokenTree::Subtree(subtree)) .collect::>(); - log::warn!("Token trees after expansion: {:#?}", tts); - - None + if !tts.is_empty() { + let tt = tt::Subtree{ delimiter: tt::Delimiter::None, token_trees: tts }; + Some(tt) + } else { + None + } } -// TODO: try iterate without allocation fn collect_trait_names(attr_node: &ast::Attr) -> Option> { if let Some((_, tt)) = attr_node.as_call() { let items = tt @@ -67,25 +61,12 @@ fn item_name(item: &ast::ModuleItem) -> Option { } } -fn expand_debug(target: &ast::ModuleItem) -> Option { +fn implement_trait_simple(trait_name: &str, target: &ast::ModuleItem) -> Option { if let Some(name) = item_name(target) { - log::warn!("Implementing Debug for {}", name); - } + let impl_code = format!("impl {} for {} {{}}", trait_name, name); + let tt = mbe::text_to_tokentree(&impl_code); - None -} - -fn expand_copy(target: &ast::ModuleItem) -> Option { - if let Some(name) = item_name(target) { - log::warn!("Implementing Copy for {}", name); - } - - None -} - -fn expand_clone(target: &ast::ModuleItem) -> Option { - if let Some(name) = item_name(target) { - log::warn!("Implementing Clone for {}", name); + return Some(tt) } None diff --git a/crates/ra_hir/src/attr_macros/tests.rs b/crates/ra_hir/src/attr_macros/tests.rs new file mode 100644 index 000000000000..bc7c83c011b4 --- /dev/null +++ b/crates/ra_hir/src/attr_macros/tests.rs @@ -0,0 +1 @@ +mod derive; diff --git a/crates/ra_hir/src/attr_macros/tests/derive.rs b/crates/ra_hir/src/attr_macros/tests/derive.rs index e69de29bb2d1..71cef89072bb 100644 --- a/crates/ra_hir/src/attr_macros/tests/derive.rs +++ b/crates/ra_hir/src/attr_macros/tests/derive.rs @@ -0,0 +1,65 @@ +use ra_syntax::{ast, AstNode, SyntaxNode}; +use crate::attr_macros::expand_attr_macro; +use crate::name::AsName; + +fn expand_attr_macro_for(code: &str) -> Option { + let ast = ast::SourceFile::parse(code) + .ok() + .unwrap() + .syntax() + .clone(); + + let attr_node = ast.descendants() + .find_map(|item| ast::Attr::cast(item)) + .unwrap(); + let target_node = ast.children() + .find_map(|item| ast::ModuleItem::cast(item)) + .unwrap(); + + expand_attr_macro(attr_node, target_node) +} + +fn has_trait_implemented(trait_name: &str, ast: &SyntaxNode) -> bool { + let impl_blocks = ast.descendants() + .map(|node| ast::ImplBlock::cast(node.clone())) + .flatten(); + + for impl_block in impl_blocks { + if let Some(ast::TypeRef::PathType(path_type)) = impl_block.target_trait() { + let name = path_type.path().unwrap() + .segment().unwrap() + .name_ref().unwrap() + .as_name().to_string(); + + if name == trait_name { + return true + } + } + } + + false +} + +#[test] +pub fn implement_trait_simple() { + let tt = expand_attr_macro_for("#[derive(Copy, Clone, Debug)] struct S {}").unwrap(); + let code = tt.to_string(); + let ast_struct = ast::SourceFile::parse(&code) + .ok() + .unwrap() + .syntax() + .clone(); + + let tt = expand_attr_macro_for("#[derive(Copy, Clone, Debug)] enum S {}").unwrap(); + let code = tt.to_string(); + let ast_enum = ast::SourceFile::parse(&code) + .ok() + .unwrap() + .syntax() + .clone(); + + for trait_name in ["Copy", "Clone", "Debug"].into_iter() { + assert!(has_trait_implemented(trait_name, &ast_struct)); + assert!(has_trait_implemented(trait_name, &ast_enum)); + } +} From 8423efab07ee58680b47f7d09e7f7b5c6e11a54f Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sun, 8 Sep 2019 03:12:38 +0800 Subject: [PATCH 16/26] rustfmt --- crates/ra_hir/src/attr_macros/derive.rs | 4 +- crates/ra_hir/src/attr_macros/tests/derive.rs | 47 +++++++------------ 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/crates/ra_hir/src/attr_macros/derive.rs b/crates/ra_hir/src/attr_macros/derive.rs index 5ca0484ce1c0..2ca3845b9626 100644 --- a/crates/ra_hir/src/attr_macros/derive.rs +++ b/crates/ra_hir/src/attr_macros/derive.rs @@ -28,7 +28,7 @@ pub(crate) fn expand_derive_attr( .collect::>(); if !tts.is_empty() { - let tt = tt::Subtree{ delimiter: tt::Delimiter::None, token_trees: tts }; + let tt = tt::Subtree { delimiter: tt::Delimiter::None, token_trees: tts }; Some(tt) } else { None @@ -66,7 +66,7 @@ fn implement_trait_simple(trait_name: &str, target: &ast::ModuleItem) -> Option< let impl_code = format!("impl {} for {} {{}}", trait_name, name); let tt = mbe::text_to_tokentree(&impl_code); - return Some(tt) + return Some(tt); } None diff --git a/crates/ra_hir/src/attr_macros/tests/derive.rs b/crates/ra_hir/src/attr_macros/tests/derive.rs index 71cef89072bb..9d39489f3219 100644 --- a/crates/ra_hir/src/attr_macros/tests/derive.rs +++ b/crates/ra_hir/src/attr_macros/tests/derive.rs @@ -1,38 +1,33 @@ -use ra_syntax::{ast, AstNode, SyntaxNode}; use crate::attr_macros::expand_attr_macro; use crate::name::AsName; +use ra_syntax::{ast, AstNode, SyntaxNode}; fn expand_attr_macro_for(code: &str) -> Option { - let ast = ast::SourceFile::parse(code) - .ok() - .unwrap() - .syntax() - .clone(); + let ast = ast::SourceFile::parse(code).ok().unwrap().syntax().clone(); - let attr_node = ast.descendants() - .find_map(|item| ast::Attr::cast(item)) - .unwrap(); - let target_node = ast.children() - .find_map(|item| ast::ModuleItem::cast(item)) - .unwrap(); + let attr_node = ast.descendants().find_map(|item| ast::Attr::cast(item)).unwrap(); + let target_node = ast.children().find_map(|item| ast::ModuleItem::cast(item)).unwrap(); expand_attr_macro(attr_node, target_node) } fn has_trait_implemented(trait_name: &str, ast: &SyntaxNode) -> bool { - let impl_blocks = ast.descendants() - .map(|node| ast::ImplBlock::cast(node.clone())) - .flatten(); + let impl_blocks = ast.descendants().map(|node| ast::ImplBlock::cast(node.clone())).flatten(); for impl_block in impl_blocks { if let Some(ast::TypeRef::PathType(path_type)) = impl_block.target_trait() { - let name = path_type.path().unwrap() - .segment().unwrap() - .name_ref().unwrap() - .as_name().to_string(); + let name = path_type + .path() + .unwrap() + .segment() + .unwrap() + .name_ref() + .unwrap() + .as_name() + .to_string(); if name == trait_name { - return true + return true; } } } @@ -44,19 +39,11 @@ fn has_trait_implemented(trait_name: &str, ast: &SyntaxNode) -> bool { pub fn implement_trait_simple() { let tt = expand_attr_macro_for("#[derive(Copy, Clone, Debug)] struct S {}").unwrap(); let code = tt.to_string(); - let ast_struct = ast::SourceFile::parse(&code) - .ok() - .unwrap() - .syntax() - .clone(); + let ast_struct = ast::SourceFile::parse(&code).ok().unwrap().syntax().clone(); let tt = expand_attr_macro_for("#[derive(Copy, Clone, Debug)] enum S {}").unwrap(); let code = tt.to_string(); - let ast_enum = ast::SourceFile::parse(&code) - .ok() - .unwrap() - .syntax() - .clone(); + let ast_enum = ast::SourceFile::parse(&code).ok().unwrap().syntax().clone(); for trait_name in ["Copy", "Clone", "Debug"].into_iter() { assert!(has_trait_implemented(trait_name, &ast_struct)); From 04d5509ce153690a0a744bf7f9370340432b61b8 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sun, 8 Sep 2019 14:35:08 +0800 Subject: [PATCH 17/26] Rename collect_derive_attr to collect_attrs --- crates/ra_syntax/src/ast/traits.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index 105087bac0c6..5ff372fec1b5 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs @@ -69,7 +69,7 @@ pub trait ModuleItemOwner: AstNode { #[derive(Debug)] pub struct ItemOrMacroIter(SyntaxNodeChildren); -fn collect_derive_attr(item: ast::ModuleItem) -> Option { +fn collect_attrs(item: ast::ModuleItem) -> Option { let item_node = item.syntax(); let mut attrs = None; @@ -97,7 +97,7 @@ impl Iterator for ItemOrMacroIter { let n = self.0.next()?; if let Some(item) = ast::ModuleItem::cast(n.clone()) { - if let Some(item) = collect_derive_attr(item.clone()) { + if let Some(item) = collect_attrs(item.clone()) { return Some(item); } From 14b6a4e4f06cbd7187f31e3e2442321215f7fe80 Mon Sep 17 00:00:00 2001 From: Evgenii P Date: Sun, 8 Sep 2019 20:00:23 +0800 Subject: [PATCH 18/26] Add test for #[derive(Trait)] type inference --- crates/ra_hir/src/ty/tests.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 3ac1fbdd50df..e8805e18178b 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -2967,6 +2967,28 @@ fn main() { ); } +#[test] +fn derive_macro_expanded() { + let (db, pos) = MockDatabase::with_position( + r#" +//- /main.rs +trait Clone { + fn clone(&self) -> Self; +} + +#[derive(Clone)] +struct S {} + +fn test() { + let s = S {}; + s.clone()<|>; +} +"#, + ); + + assert_eq!("S", type_at_pos(&db, pos)); +} + #[test] fn infer_type_value_macro_having_same_name() { assert_snapshot!( From 5bb0d4796b99889f4746654a90240ec5108d0d4c Mon Sep 17 00:00:00 2001 From: Evgenii Ponomarev Date: Mon, 23 Sep 2019 16:57:53 +0800 Subject: [PATCH 19/26] Fix after rebase --- crates/ra_hir/src/expr/lower.rs | 2 +- crates/ra_hir/src/ids.rs | 2 +- crates/ra_hir/src/nameres/collector.rs | 8 ++++++++ crates/ra_hir/src/ty/tests.rs | 3 +++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/crates/ra_hir/src/expr/lower.rs b/crates/ra_hir/src/expr/lower.rs index 61535d24fe12..a6797860cee4 100644 --- a/crates/ra_hir/src/expr/lower.rs +++ b/crates/ra_hir/src/expr/lower.rs @@ -461,7 +461,7 @@ where if let Some(path) = e.path().and_then(Path::from_ast) { if let Some(def) = self.resolver.resolve_path_as_macro(self.db, &path) { - let call_id = MacroCallLoc { def: def.id, ast_id }.id(self.db); + let call_id = MacroCallLoc::Macro { def: def.id, ast_id }.id(self.db); let file_id = call_id.as_file(MacroFileKind::Expr); if let Some(node) = self.db.parse_or_expand(file_id) { if let Some(expr) = ast::Expr::cast(node) { diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 562ba3504e45..9416ca231172 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -10,7 +10,7 @@ use ra_syntax::{ast, AstNode, Parse, SyntaxNode}; use crate::{ db::{AstDatabase, DefDatabase, InternDatabase}, - AstId, FileAstId, Module, Source, + AstId, FileAstId, Module, Source, attr_macros, }; /// hir makes heavy use of ids: integer (u32) handlers to various things. You diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index 002087e85e90..fa5f15d09317 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -719,11 +719,19 @@ where module_id: self.module_id, raw_items: &raw_items, parent_module: None, + attr_path: None, } .collect(raw_items.items()); } } } + + fn import_all_legacy_macros(&mut self, module_id: CrateModuleId) { + let macros = self.def_collector.def_map[module_id].scope.legacy_macros.clone(); + for (name, macro_) in macros { + self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_); + } + } } fn is_macro_rules(path: &Path) -> bool { diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index e8805e18178b..66271e40a201 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -4196,6 +4196,9 @@ fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); let analyzer = SourceAnalyzer::new(db, pos.file_id, expr.syntax(), Some(pos.offset)); let ty = analyzer.type_of(db, &expr).unwrap(); + + dbg!(&file, &expr, &analyzer, &ty); + ty.display(db).to_string() } From 0d177cea1d6347082862a84d95a69566938f35c6 Mon Sep 17 00:00:00 2001 From: Evgenii Ponomarev Date: Mon, 23 Sep 2019 16:58:38 +0800 Subject: [PATCH 20/26] Fix parsing token trees produced by macro expansion --- crates/ra_parser/src/grammar.rs | 9 +++++++++ crates/ra_parser/src/grammar/items.rs | 2 ++ 2 files changed, 11 insertions(+) diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs index e2355aff9a5d..317fd036b622 100644 --- a/crates/ra_parser/src/grammar.rs +++ b/crates/ra_parser/src/grammar.rs @@ -117,8 +117,17 @@ pub(crate) mod fragments { pub(crate) fn macro_items(p: &mut Parser) { let m = p.start(); + if p.at(L_DOLLAR) { + p.bump(L_DOLLAR); + } + items::mod_contents(p, false); + m.complete(p, MACRO_ITEMS); + + if p.at(R_DOLLAR) { + p.bump(R_DOLLAR); + } } pub(crate) fn macro_stmts(p: &mut Parser) { diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index 4c67a5c2e218..ba43188af692 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs @@ -66,6 +66,8 @@ pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemF p.error("unmatched `}`"); p.bump(T!['}']); e.complete(p, ERROR); + } else if p.at(R_DOLLAR) { + p.bump(R_DOLLAR); } else if !p.at(EOF) && !p.at(T!['}']) { p.err_and_bump("expected an item"); } else { From f7eaa31d832b443ccd685813237b085fdd56f223 Mon Sep 17 00:00:00 2001 From: Evgenii Ponomarev Date: Mon, 23 Sep 2019 17:01:27 +0800 Subject: [PATCH 21/26] Remove dbg! from tests --- crates/ra_hir/src/ty/tests.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 66271e40a201..e8805e18178b 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -4196,9 +4196,6 @@ fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); let analyzer = SourceAnalyzer::new(db, pos.file_id, expr.syntax(), Some(pos.offset)); let ty = analyzer.type_of(db, &expr).unwrap(); - - dbg!(&file, &expr, &analyzer, &ty); - ty.display(db).to_string() } From dbf2898d08fec06589fbd376ead706add0fa8553 Mon Sep 17 00:00:00 2001 From: Evgenii Ponomarev Date: Mon, 23 Sep 2019 17:02:41 +0800 Subject: [PATCH 22/26] Implement collection of impl blocks from #[derive] attribute macro --- crates/ra_hir/src/impl_block.rs | 35 ++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index d830202bd268..5ce8107e24a4 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -217,16 +217,33 @@ impl ModuleImplBlocks { ast::ItemOrMacro::Item(_) => (), ast::ItemOrMacro::Macro(macro_call) => { //FIXME: we should really cut down on the boilerplate required to process a macro - let ast_id = db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id); - if let Some(path) = macro_call.path().and_then(Path::from_ast) { - if let Some(def) = self.module.resolver(db).resolve_path_as_macro(db, &path) - { - let call_id = MacroCallLoc { def: def.id, ast_id }.id(db); - let file_id = call_id.as_file(MacroFileKind::Items); + match macro_call { + ast::MacroKind::Call(macro_call) => { + let ast_id = db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id); + if let Some(path) = macro_call.path().and_then(Path::from_ast) { + if let Some(def) = self.module.resolver(db).resolve_path_as_macro(db, &path) + { + let call_id = MacroCallLoc::Macro { def: def.id, ast_id }.id(db); + let file_id = call_id.as_file(MacroFileKind::Items); + if let Some(item_list) = + db.parse_or_expand(file_id).and_then(ast::MacroItems::cast) + { + self.collect_from_item_owner(db, source_map, &item_list, file_id) + } + } + } + } + + ast::MacroKind::Attr { attr, target } => { + let attr_ast_id = db.ast_id_map(file_id).ast_id(&attr).with_file_id(file_id); + let target_ast_id = db.ast_id_map(file_id).ast_id(&target).with_file_id(file_id); + + let call_loc = MacroCallLoc::Attribute { attr_ast_id, target_ast_id }.id(db); + let file_id = call_loc.as_file(MacroFileKind::Items); + if let Some(item_list) = - db.parse_or_expand(file_id).and_then(ast::MacroItems::cast) - { - self.collect_from_item_owner(db, source_map, &item_list, file_id) + db.parse_or_expand(file_id).and_then(ast::MacroItems::cast) { + self.collect_from_item_owner(db, source_map, &item_list, file_id); } } } From 523774ac341e51cb23f9d4e2e05af00b80b90978 Mon Sep 17 00:00:00 2001 From: Evgenii Ponomarev Date: Mon, 23 Sep 2019 17:03:34 +0800 Subject: [PATCH 23/26] Add target item back to the scope since it was consumed by attr macro --- crates/ra_hir/src/nameres/raw.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 08e169b068b5..bb63cbde7556 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -379,6 +379,16 @@ impl RawItemsCollector { .alloc(MacroData::Attr(AttrData { attr_ast_id, target_ast_id })); self.push_item(current_module, RawItem::Macro(m)); + + // Since attribute macro also captures the item it's applied to, + // for example: + // -----------------\ /-------------------- + // #[derive(Clone)] | ----> expansion ---> | impl Clone for S {} + // struct S {} | | + // -----------------/ \-------------------- + // we must collect captured item separately, in order to preserve it + // after attribute macro expansion: + self.add_item(current_module, target); } } } From aa6d8b95f4301ed1e054b3c05bbe0d1fbe677647 Mon Sep 17 00:00:00 2001 From: Evgenii Ponomarev Date: Mon, 23 Sep 2019 17:41:51 +0800 Subject: [PATCH 24/26] Fix post-rebase regression --- crates/ra_hir/src/nameres/raw.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index bb63cbde7556..4dc1d369e6ef 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -359,7 +359,8 @@ impl RawItemsCollector { let name = m.name().map(|it| it.as_name()); let ast_id = self.source_ast_id_map.ast_id(&m); - let export = m.has_atom_attr("macro_export"); + let export = m.has_atom_attr("macro_export") + || m.attrs().filter_map(|x| x.as_call()).any(|(name, _)| name == "macro_export"); let m = self.raw_items.macros.alloc(MacroData::MacroCall(MacroCallData { ast_id, path, From 6b2474d0048cd85810ec31ed657bea0559a67454 Mon Sep 17 00:00:00 2001 From: Evgenii Ponomarev Date: Mon, 23 Sep 2019 17:53:50 +0800 Subject: [PATCH 25/26] rustfmt --- crates/ra_hir/src/ids.rs | 3 ++- crates/ra_hir/src/impl_block.rs | 27 +++++++++++++++++--------- crates/ra_hir/src/nameres/collector.rs | 13 +++++-------- crates/ra_hir/src/nameres/raw.rs | 4 +++- crates/ra_mbe/src/lib.rs | 5 ++--- 5 files changed, 30 insertions(+), 22 deletions(-) diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs index 9416ca231172..05d879f6eee2 100644 --- a/crates/ra_hir/src/ids.rs +++ b/crates/ra_hir/src/ids.rs @@ -9,8 +9,9 @@ use ra_prof::profile; use ra_syntax::{ast, AstNode, Parse, SyntaxNode}; use crate::{ + attr_macros, db::{AstDatabase, DefDatabase, InternDatabase}, - AstId, FileAstId, Module, Source, attr_macros, + AstId, FileAstId, Module, Source, }; /// hir makes heavy use of ids: integer (u32) handlers to various things. You diff --git a/crates/ra_hir/src/impl_block.rs b/crates/ra_hir/src/impl_block.rs index 5ce8107e24a4..73bb64d309be 100644 --- a/crates/ra_hir/src/impl_block.rs +++ b/crates/ra_hir/src/impl_block.rs @@ -219,30 +219,39 @@ impl ModuleImplBlocks { //FIXME: we should really cut down on the boilerplate required to process a macro match macro_call { ast::MacroKind::Call(macro_call) => { - let ast_id = db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id); + let ast_id = + db.ast_id_map(file_id).ast_id(¯o_call).with_file_id(file_id); if let Some(path) = macro_call.path().and_then(Path::from_ast) { - if let Some(def) = self.module.resolver(db).resolve_path_as_macro(db, &path) + if let Some(def) = + self.module.resolver(db).resolve_path_as_macro(db, &path) { - let call_id = MacroCallLoc::Macro { def: def.id, ast_id }.id(db); + let call_id = + MacroCallLoc::Macro { def: def.id, ast_id }.id(db); let file_id = call_id.as_file(MacroFileKind::Items); if let Some(item_list) = - db.parse_or_expand(file_id).and_then(ast::MacroItems::cast) + db.parse_or_expand(file_id).and_then(ast::MacroItems::cast) { - self.collect_from_item_owner(db, source_map, &item_list, file_id) + self.collect_from_item_owner( + db, source_map, &item_list, file_id, + ) } } } } ast::MacroKind::Attr { attr, target } => { - let attr_ast_id = db.ast_id_map(file_id).ast_id(&attr).with_file_id(file_id); - let target_ast_id = db.ast_id_map(file_id).ast_id(&target).with_file_id(file_id); + let attr_ast_id = + db.ast_id_map(file_id).ast_id(&attr).with_file_id(file_id); + let target_ast_id = + db.ast_id_map(file_id).ast_id(&target).with_file_id(file_id); - let call_loc = MacroCallLoc::Attribute { attr_ast_id, target_ast_id }.id(db); + let call_loc = + MacroCallLoc::Attribute { attr_ast_id, target_ast_id }.id(db); let file_id = call_loc.as_file(MacroFileKind::Items); if let Some(item_list) = - db.parse_or_expand(file_id).and_then(ast::MacroItems::cast) { + db.parse_or_expand(file_id).and_then(ast::MacroItems::cast) + { self.collect_from_item_owner(db, source_map, &item_list, file_id); } } diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs index fa5f15d09317..ed1325b0fa84 100644 --- a/crates/ra_hir/src/nameres/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -559,7 +559,7 @@ where raw_items: self.raw_items, parent_module: Some(parent_module), } - .collect(&*items); + .collect(&*items); if *is_macro_use { self.import_all_legacy_macros(module_id); } @@ -588,7 +588,7 @@ where raw_items: &raw_items, parent_module: None, } - .collect(raw_items.items()); + .collect(raw_items.items()); if *is_macro_use { self.import_all_legacy_macros(module_id); } @@ -680,12 +680,9 @@ where // Case 2: try to resolve in legacy scope and expand macro_rules, triggering // recursive item collection. - if let Some(macro_def) = mac - .path - .as_ident() - .and_then(|name| { - self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) - }) { + if let Some(macro_def) = mac.path.as_ident().and_then(|name| { + self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) + }) { let def = macro_def.id; let macro_call_id = MacroCallLoc::Macro { def, ast_id }.id(self.def_collector.db); diff --git a/crates/ra_hir/src/nameres/raw.rs b/crates/ra_hir/src/nameres/raw.rs index 4dc1d369e6ef..7482af7baf5f 100644 --- a/crates/ra_hir/src/nameres/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -360,7 +360,9 @@ impl RawItemsCollector { let name = m.name().map(|it| it.as_name()); let ast_id = self.source_ast_id_map.ast_id(&m); let export = m.has_atom_attr("macro_export") - || m.attrs().filter_map(|x| x.as_call()).any(|(name, _)| name == "macro_export"); + || m.attrs() + .filter_map(|x| x.as_call()) + .any(|(name, _)| name == "macro_export"); let m = self.raw_items.macros.alloc(MacroData::MacroCall(MacroCallData { ast_id, path, diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index 12dbca0e7eea..c958bc5c45b7 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs @@ -31,9 +31,8 @@ pub enum ExpandError { } pub use crate::syntax_bridge::{ - ast_to_token_tree, syntax_node_to_token_tree, token_tree_to_expr, token_tree_to_items, - token_tree_to_macro_stmts, token_tree_to_pat, token_tree_to_ty, - text_to_tokentree, + ast_to_token_tree, syntax_node_to_token_tree, text_to_tokentree, token_tree_to_expr, + token_tree_to_items, token_tree_to_macro_stmts, token_tree_to_pat, token_tree_to_ty, }; /// This struct contains AST for a single `macro_rules` definition. What might From 8c813bc9551837cc7c8ff9cc79ce1ddb377d5905 Mon Sep 17 00:00:00 2001 From: Evgenii Ponomarev Date: Mon, 23 Sep 2019 17:56:15 +0800 Subject: [PATCH 26/26] TODO -> FIXME --- crates/ra_hir/src/attr_macros/derive.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_hir/src/attr_macros/derive.rs b/crates/ra_hir/src/attr_macros/derive.rs index 2ca3845b9626..cb0d38c31f63 100644 --- a/crates/ra_hir/src/attr_macros/derive.rs +++ b/crates/ra_hir/src/attr_macros/derive.rs @@ -6,7 +6,7 @@ use ra_syntax::{ /// Checks whether `#[attr]` is in the `#[derive()]` form. pub(crate) fn is_derive_attr(attr_node: &ast::Attr) -> bool { if let Some((name, _args)) = attr_node.as_call() { - // TODO: check for empty _args tree + // FIXME: check for empty _args tree name == "derive" } else { false