Add basic custom derive lowering
This commit is contained in:
parent
e2dd17f75b
commit
34dc8d25c1
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -1081,6 +1081,14 @@ dependencies = [
|
||||
"drop_bomb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ra_proc_macro"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ra_mbe",
|
||||
"ra_tt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ra_prof"
|
||||
version = "0.1.0"
|
||||
|
@ -475,6 +475,9 @@ impl AsMacroCall for AstIdWithPath<ast::ModuleItem> {
|
||||
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
|
||||
) -> Option<MacroCallId> {
|
||||
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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -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<MacroDefId> {
|
||||
// FIXME this is currently super hacky, just enough to support the
|
||||
// built-in derives
|
||||
fn resolve_attribute_macro(
|
||||
&self,
|
||||
directive: &DeriveDirective,
|
||||
path: &ModPath,
|
||||
) -> Option<MacroDefId> {
|
||||
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());
|
||||
|
||||
|
@ -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<A, B>;
|
||||
"#,
|
||||
BuiltinDeriveExpander::Copy,
|
||||
name::known::Copy,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@ -292,7 +290,7 @@ mod tests {
|
||||
#[derive(Copy)]
|
||||
struct Foo<A, B, 'a, 'b>;
|
||||
"#,
|
||||
BuiltinDeriveExpander::Copy,
|
||||
name::known::Copy,
|
||||
);
|
||||
|
||||
// We currently just ignore lifetimes
|
||||
@ -310,7 +308,7 @@ mod tests {
|
||||
#[derive(Clone)]
|
||||
struct Foo<A, B>;
|
||||
"#,
|
||||
BuiltinDeriveExpander::Clone,
|
||||
name::known::Clone,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
|
@ -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())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)?
|
||||
|
@ -30,6 +30,7 @@ impl Hygiene {
|
||||
MacroDefKind::BuiltIn(_) => None,
|
||||
MacroDefKind::BuiltInDerive(_) => None,
|
||||
MacroDefKind::BuiltInEager(_) => None,
|
||||
MacroDefKind::ProcMacro(_) => None,
|
||||
}
|
||||
}
|
||||
MacroCallId::EagerMacro(_id) => None,
|
||||
|
@ -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<ast::MacroCall>),
|
||||
Attr(AstId<ast::ModuleItem>),
|
||||
Attr(AstId<ast::ModuleItem>, 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<SyntaxNode> {
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
32
crates/ra_hir_expand/src/proc_macro.rs
Normal file
32
crates/ra_hir_expand/src/proc_macro.rs
Normal file
@ -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<tt::Subtree, mbe::ExpandError> {
|
||||
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!()
|
||||
}
|
||||
}
|
@ -639,3 +639,26 @@ mod clone {
|
||||
);
|
||||
assert_eq!("(Wrapper<S>, {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));
|
||||
}
|
||||
|
@ -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)>,
|
||||
|
Loading…
x
Reference in New Issue
Block a user