From 34dc8d25c1e461cc311d6d4404f74502513cd3ae Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Wed, 18 Mar 2020 17:47:59 +0800 Subject: Add basic custom derive lowering --- crates/ra_hir_def/src/lib.rs | 5 ++- crates/ra_hir_def/src/nameres/collector.rs | 60 ++++++++++++++++++++++++++---- crates/ra_hir_expand/src/builtin_derive.rs | 22 +++++------ crates/ra_hir_expand/src/db.rs | 11 +++++- crates/ra_hir_expand/src/eager.rs | 3 +- crates/ra_hir_expand/src/hygiene.rs | 1 + crates/ra_hir_expand/src/lib.rs | 13 +++++-- crates/ra_hir_expand/src/proc_macro.rs | 32 ++++++++++++++++ crates/ra_hir_ty/src/tests/macros.rs | 23 ++++++++++++ crates/ra_mbe/src/syntax_bridge.rs | 2 +- 10 files changed, 144 insertions(+), 28 deletions(-) create mode 100644 crates/ra_hir_expand/src/proc_macro.rs (limited to 'crates') diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 516dd773e..b3e811671 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -475,6 +475,9 @@ impl AsMacroCall for AstIdWithPath { resolver: impl Fn(path::ModPath) -> Option, ) -> Option { let def = resolver(self.path.clone())?; - Some(def.as_lazy_macro(db.upcast(), MacroCallKind::Attr(self.ast_id)).into()) + Some( + def.as_lazy_macro(db.upcast(), MacroCallKind::Attr(self.ast_id, self.path.to_string())) + .into(), + ) } } diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 5b292c250..76be71a30 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs @@ -7,6 +7,7 @@ use hir_expand::{ builtin_derive::find_builtin_derive, builtin_macro::find_builtin_macro, name::{name, AsName, Name}, + proc_macro::ProcMacroExpander, HirFileId, MacroCallId, MacroDefId, MacroDefKind, }; use ra_cfg::CfgOptions; @@ -238,6 +239,18 @@ impl DefCollector<'_> { self.def_map.modules[module_id].scope.define_legacy_macro(name, mac); } + /// Define a proc macro + /// + /// A proc macro is similar to normal macro scope, but it would not visiable in legacy textual scoped. + /// And unconditionally exported. + fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) { + self.update( + self.def_map.root, + &[(name, PerNs::macros(macro_, Visibility::Public))], + Visibility::Public, + ); + } + /// Import macros from `#[macro_use] extern crate`. fn import_macros_from_extern_crate( &mut self, @@ -537,8 +550,9 @@ impl DefCollector<'_> { true }); attribute_macros.retain(|directive| { - if let Some(call_id) = - directive.ast_id.as_call_id(self.db, |path| self.resolve_attribute_macro(&path)) + if let Some(call_id) = directive + .ast_id + .as_call_id(self.db, |path| self.resolve_attribute_macro(&directive, &path)) { resolved.push((directive.module_id, call_id, 0)); res = ReachedFixedPoint::No; @@ -562,9 +576,11 @@ impl DefCollector<'_> { res } - fn resolve_attribute_macro(&self, path: &ModPath) -> Option { - // FIXME this is currently super hacky, just enough to support the - // built-in derives + fn resolve_attribute_macro( + &self, + directive: &DeriveDirective, + path: &ModPath, + ) -> Option { if let Some(name) = path.as_ident() { // FIXME this should actually be handled with the normal name // resolution; the std lib defines built-in stubs for the derives, @@ -573,7 +589,15 @@ impl DefCollector<'_> { return Some(def_id); } } - None + let resolved_res = self.def_map.resolve_path_fp_with_macro( + self.db, + ResolveMode::Other, + directive.module_id, + &path, + BuiltinShadowMode::Module, + ); + + resolved_res.resolved_def.take_macros() } fn collect_macro_expansion( @@ -776,8 +800,8 @@ impl ModCollector<'_, '_> { // FIXME: check attrs to see if this is an attribute macro invocation; // in which case we don't add the invocation, just a single attribute // macro invocation - self.collect_derives(attrs, def); + self.collect_proc_macro(attrs); let name = def.name.clone(); let container = ContainerId::ModuleId(module); @@ -854,6 +878,28 @@ impl ModCollector<'_, '_> { } } + fn collect_proc_macro(&mut self, attrs: &Attrs) { + if let Some(derive_subtree) = attrs.by_key("proc_macro_derive").tt_values().next() { + if let Some(tt) = derive_subtree.token_trees.get(0) { + let ident = match &tt { + tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident, + _ => return, // anything else would be an error (which we currently ignore) + }; + let name = ident.as_name(); + let krate = self.def_collector.def_map.krate; + let expander = ProcMacroExpander::new(krate); + + let macro_id = MacroDefId { + ast_id: None, + krate: Some(krate), + kind: MacroDefKind::ProcMacro(expander), + }; + + self.def_collector.define_proc_macro(name.clone(), macro_id); + } + } + } + fn collect_macro(&mut self, mac: &raw::MacroData) { let mut ast_id = AstIdWithPath::new(self.file_id, mac.ast_id, mac.path.clone()); diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs index a5b50a832..7f753bbca 100644 --- a/crates/ra_hir_expand/src/builtin_derive.rs +++ b/crates/ra_hir_expand/src/builtin_derive.rs @@ -229,9 +229,12 @@ fn partial_ord_expand( mod tests { use super::*; use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc}; + use name::Name; use ra_db::{fixture::WithFixture, SourceDatabase}; - fn expand_builtin_derive(s: &str, expander: BuiltinDeriveExpander) -> String { + fn expand_builtin_derive(s: &str, name: Name) -> String { + let def = find_builtin_derive(&name).unwrap(); + let (db, file_id) = TestDB::with_single_file(&s); let parsed = db.parse(file_id); let items: Vec<_> = @@ -239,14 +242,9 @@ mod tests { let ast_id_map = db.ast_id_map(file_id.into()); - // the first one should be a macro_rules - let def = - MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(expander) }; + let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0])); - let loc = MacroCallLoc { - def, - kind: MacroCallKind::Attr(AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]))), - }; + let loc = MacroCallLoc { def, kind: MacroCallKind::Attr(attr_id, name.to_string()) }; let id: MacroCallId = db.intern_macro(loc).into(); let parsed = db.parse_or_expand(id.as_file()).unwrap(); @@ -263,7 +261,7 @@ mod tests { #[derive(Copy)] struct Foo; "#, - BuiltinDeriveExpander::Copy, + name::known::Copy, ); assert_eq!(expanded, "impl< >std::marker::CopyforFoo< >{}"); @@ -276,7 +274,7 @@ mod tests { #[derive(Copy)] struct Foo; "#, - BuiltinDeriveExpander::Copy, + name::known::Copy, ); assert_eq!( @@ -292,7 +290,7 @@ mod tests { #[derive(Copy)] struct Foo; "#, - BuiltinDeriveExpander::Copy, + name::known::Copy, ); // We currently just ignore lifetimes @@ -310,7 +308,7 @@ mod tests { #[derive(Clone)] struct Foo; "#, - BuiltinDeriveExpander::Clone, + name::known::Clone, ); assert_eq!( diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index 5a696542f..c2eb75ee5 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs @@ -11,7 +11,7 @@ use ra_syntax::{algo::diff, AstNode, Parse, SyntaxKind::*, SyntaxNode}; use crate::{ ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, - MacroFile, + MacroFile, ProcMacroExpander, }; #[derive(Debug, Clone, Eq, PartialEq)] @@ -19,6 +19,7 @@ pub enum TokenExpander { MacroRules(mbe::MacroRules), Builtin(BuiltinFnLikeExpander), BuiltinDerive(BuiltinDeriveExpander), + ProcMacro(ProcMacroExpander), } impl TokenExpander { @@ -33,6 +34,7 @@ impl TokenExpander { // FIXME switch these to ExpandResult as well TokenExpander::Builtin(it) => it.expand(db, id, tt).into(), TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), + TokenExpander::ProcMacro(it) => it.expand(db, id, tt).into(), } } @@ -41,6 +43,7 @@ impl TokenExpander { TokenExpander::MacroRules(it) => it.map_id_down(id), TokenExpander::Builtin(..) => id, TokenExpander::BuiltinDerive(..) => id, + TokenExpander::ProcMacro(..) => id, } } @@ -49,6 +52,7 @@ impl TokenExpander { TokenExpander::MacroRules(it) => it.map_id_up(id), TokenExpander::Builtin(..) => (id, mbe::Origin::Call), TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call), + TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call), } } } @@ -130,7 +134,10 @@ pub(crate) fn macro_def( MacroDefKind::BuiltInDerive(expander) => { Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) } - MacroDefKind::BuiltInEager(_expander) => None, + MacroDefKind::BuiltInEager(_) => None, + MacroDefKind::ProcMacro(expander) => { + Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default()))) + } } } diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs index 687d40294..9ff743b9e 100644 --- a/crates/ra_hir_expand/src/eager.rs +++ b/crates/ra_hir_expand/src/eager.rs @@ -112,7 +112,8 @@ fn eager_macro_recur( } MacroDefKind::Declarative | MacroDefKind::BuiltIn(_) - | MacroDefKind::BuiltInDerive(_) => { + | MacroDefKind::BuiltInDerive(_) + | MacroDefKind::ProcMacro(_) => { let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?; // replace macro inside eager_macro_recur(db, expanded, macro_resolver)? diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs index dfbac494f..182c08eb3 100644 --- a/crates/ra_hir_expand/src/hygiene.rs +++ b/crates/ra_hir_expand/src/hygiene.rs @@ -30,6 +30,7 @@ impl Hygiene { MacroDefKind::BuiltIn(_) => None, MacroDefKind::BuiltInDerive(_) => None, MacroDefKind::BuiltInEager(_) => None, + MacroDefKind::ProcMacro(_) => None, } } MacroCallId::EagerMacro(_id) => None, diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 6b59ea4c9..ac1d12252 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs @@ -11,6 +11,7 @@ pub mod hygiene; pub mod diagnostics; pub mod builtin_derive; pub mod builtin_macro; +pub mod proc_macro; pub mod quote; pub mod eager; @@ -27,6 +28,7 @@ use ra_syntax::{ use crate::ast_id_map::FileAstId; use crate::builtin_derive::BuiltinDeriveExpander; use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander}; +use crate::proc_macro::ProcMacroExpander; #[cfg(test)] mod test_db; @@ -217,6 +219,7 @@ pub enum MacroDefKind { // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander BuiltInDerive(BuiltinDeriveExpander), BuiltInEager(EagerExpander), + ProcMacro(ProcMacroExpander), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -228,21 +231,23 @@ pub struct MacroCallLoc { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum MacroCallKind { FnLike(AstId), - Attr(AstId), + Attr(AstId, String), } impl MacroCallKind { pub fn file_id(&self) -> HirFileId { match self { MacroCallKind::FnLike(ast_id) => ast_id.file_id, - MacroCallKind::Attr(ast_id) => ast_id.file_id, + MacroCallKind::Attr(ast_id, _) => ast_id.file_id, } } pub fn node(&self, db: &dyn db::AstDatabase) -> InFile { match self { MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), - MacroCallKind::Attr(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), + MacroCallKind::Attr(ast_id, _) => { + ast_id.with_value(ast_id.to_node(db).syntax().clone()) + } } } @@ -251,7 +256,7 @@ impl MacroCallKind { MacroCallKind::FnLike(ast_id) => { Some(ast_id.to_node(db).token_tree()?.syntax().clone()) } - MacroCallKind::Attr(ast_id) => Some(ast_id.to_node(db).syntax().clone()), + MacroCallKind::Attr(ast_id, _) => Some(ast_id.to_node(db).syntax().clone()), } } } diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs new file mode 100644 index 000000000..68c828790 --- /dev/null +++ b/crates/ra_hir_expand/src/proc_macro.rs @@ -0,0 +1,32 @@ +//! Proc Macro Expander stub + +use crate::{db::AstDatabase, LazyMacroId, MacroCallKind, MacroCallLoc}; +use ra_db::CrateId; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct ProcMacroExpander { + krate: CrateId, +} + +impl ProcMacroExpander { + pub fn new(krate: CrateId) -> ProcMacroExpander { + ProcMacroExpander { krate } + } + + pub fn expand( + &self, + db: &dyn AstDatabase, + id: LazyMacroId, + _tt: &tt::Subtree, + ) -> Result { + let loc: MacroCallLoc = db.lookup_intern_macro(id); + let name = match loc.kind { + MacroCallKind::FnLike(_) => return Err(mbe::ExpandError::ConversionError), + MacroCallKind::Attr(_, name) => name, + }; + + dbg!(name); + + unimplemented!() + } +} diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 2e309a379..d59e4eea6 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs @@ -639,3 +639,26 @@ mod clone { ); assert_eq!("(Wrapper, {unknown})", type_at_pos(&db, pos)); } + +#[test] +fn infer_custom_derive_simple() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs crate:main deps:foo +use foo::Foo; + +#[derive(Foo)] +struct S{} + +fn test() { + S{}<|>; +} + +//- /lib.rs crate:foo +#[proc_macro_derive(Foo)] +pub fn derive_foo(_item: TokenStream) -> TokenStream { +} +"#, + ); + assert_eq!("S", type_at_pos(&db, pos)); +} diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index e3cde9eed..8e8ae2b29 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs @@ -33,7 +33,7 @@ impl TokenTextRange { } /// Maps `tt::TokenId` to the relative range of the original token. -#[derive(Debug, PartialEq, Eq, Default)] +#[derive(Debug, PartialEq, Eq, Clone, Default)] pub struct TokenMap { /// Maps `tt::TokenId` to the *relative* source range. entries: Vec<(tt::TokenId, TokenTextRange)>, -- cgit v1.2.3