mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-02 22:54:58 +00:00
Support local macro_rules
This commit is contained in:
parent
4c85e53531
commit
fe78a14bbb
6 changed files with 96 additions and 16 deletions
|
@ -47,13 +47,19 @@ impl Expander {
|
||||||
pub(crate) fn enter_expand<T: ast::AstNode, DB: DefDatabase>(
|
pub(crate) fn enter_expand<T: ast::AstNode, DB: DefDatabase>(
|
||||||
&mut self,
|
&mut self,
|
||||||
db: &DB,
|
db: &DB,
|
||||||
|
local_scope: Option<&ItemScope>,
|
||||||
macro_call: ast::MacroCall,
|
macro_call: ast::MacroCall,
|
||||||
) -> Option<(Mark, T)> {
|
) -> Option<(Mark, T)> {
|
||||||
let macro_call = InFile::new(self.current_file_id, ¯o_call);
|
let macro_call = InFile::new(self.current_file_id, ¯o_call);
|
||||||
|
|
||||||
if let Some(call_id) =
|
if let Some(call_id) = macro_call.as_call_id(db, |path| {
|
||||||
macro_call.as_call_id(db, |path| self.resolve_path_as_macro(db, &path))
|
if let Some(local_scope) = local_scope {
|
||||||
{
|
if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
|
||||||
|
return Some(def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.resolve_path_as_macro(db, &path)
|
||||||
|
}) {
|
||||||
let file_id = call_id.as_file();
|
let file_id = call_id.as_file();
|
||||||
if let Some(node) = db.parse_or_expand(file_id) {
|
if let Some(node) = db.parse_or_expand(file_id) {
|
||||||
if let Some(expr) = T::cast(node) {
|
if let Some(expr) = T::cast(node) {
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
|
|
||||||
use hir_expand::name::{name, AsName, Name};
|
use hir_expand::{
|
||||||
|
name::{name, AsName, Name},
|
||||||
|
MacroDefId, MacroDefKind,
|
||||||
|
};
|
||||||
use ra_arena::Arena;
|
use ra_arena::Arena;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
|
@ -452,19 +455,30 @@ where
|
||||||
None => self.alloc_expr(Expr::Missing, syntax_ptr),
|
None => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// FIXME expand to statements in statement position
|
|
||||||
ast::Expr::MacroCall(e) => {
|
ast::Expr::MacroCall(e) => {
|
||||||
let macro_call = self.expander.to_source(AstPtr::new(&e));
|
if let Some(name) = is_macro_rules(&e) {
|
||||||
match self.expander.enter_expand(self.db, e) {
|
let mac = MacroDefId {
|
||||||
Some((mark, expansion)) => {
|
krate: Some(self.expander.module.krate),
|
||||||
self.source_map
|
ast_id: Some(self.expander.ast_id(&e)),
|
||||||
.expansions
|
kind: MacroDefKind::Declarative,
|
||||||
.insert(macro_call, self.expander.current_file_id);
|
};
|
||||||
let id = self.collect_expr(expansion);
|
self.body.item_scope.define_legacy_macro(name, mac);
|
||||||
self.expander.exit(self.db, mark);
|
|
||||||
id
|
// FIXME: do we still need to allocate this as missing ?
|
||||||
|
self.alloc_expr(Expr::Missing, syntax_ptr)
|
||||||
|
} else {
|
||||||
|
let macro_call = self.expander.to_source(AstPtr::new(&e));
|
||||||
|
match self.expander.enter_expand(self.db, Some(&self.body.item_scope), e) {
|
||||||
|
Some((mark, expansion)) => {
|
||||||
|
self.source_map
|
||||||
|
.expansions
|
||||||
|
.insert(macro_call, self.expander.current_file_id);
|
||||||
|
let id = self.collect_expr(expansion);
|
||||||
|
self.expander.exit(self.db, mark);
|
||||||
|
id
|
||||||
|
}
|
||||||
|
None => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||||
}
|
}
|
||||||
None => self.alloc_expr(Expr::Missing, syntax_ptr),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,6 +700,16 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_macro_rules(m: &ast::MacroCall) -> Option<Name> {
|
||||||
|
let name = m.path()?.segment()?.name_ref()?.as_name();
|
||||||
|
|
||||||
|
if name == name![macro_rules] {
|
||||||
|
Some(m.name()?.as_name())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ast::BinOp> for BinaryOp {
|
impl From<ast::BinOp> for BinaryOp {
|
||||||
fn from(ast_op: ast::BinOp) -> Self {
|
fn from(ast_op: ast::BinOp) -> Self {
|
||||||
match ast_op {
|
match ast_op {
|
||||||
|
|
|
@ -280,7 +280,7 @@ fn collect_impl_items_in_macro(
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((mark, items)) = expander.enter_expand(db, m) {
|
if let Some((mark, items)) = expander.enter_expand(db, None, m) {
|
||||||
let items: InFile<ast::MacroItems> = expander.to_source(items);
|
let items: InFile<ast::MacroItems> = expander.to_source(items);
|
||||||
let mut res = collect_impl_items(
|
let mut res = collect_impl_items(
|
||||||
db,
|
db,
|
||||||
|
|
|
@ -381,6 +381,11 @@ impl Resolver {
|
||||||
db: &impl DefDatabase,
|
db: &impl DefDatabase,
|
||||||
path: &ModPath,
|
path: &ModPath,
|
||||||
) -> Option<MacroDefId> {
|
) -> Option<MacroDefId> {
|
||||||
|
// Search item scope legacy macro first
|
||||||
|
if let Some(def) = self.resolve_local_macro_def(path) {
|
||||||
|
return Some(def);
|
||||||
|
}
|
||||||
|
|
||||||
let (item_map, module) = self.module_scope()?;
|
let (item_map, module) = self.module_scope()?;
|
||||||
item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros()
|
item_map.resolve_path(db, module, &path, BuiltinShadowMode::Other).0.take_macros()
|
||||||
}
|
}
|
||||||
|
@ -413,6 +418,16 @@ impl Resolver {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_local_macro_def(&self, path: &ModPath) -> Option<MacroDefId> {
|
||||||
|
let name = path.as_ident()?;
|
||||||
|
self.scopes.iter().rev().find_map(|scope| {
|
||||||
|
if let Scope::LocalItemsScope(body) = scope {
|
||||||
|
return body.item_scope.get_legacy_macro(name);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn module(&self) -> Option<ModuleId> {
|
pub fn module(&self) -> Option<ModuleId> {
|
||||||
let (def_map, local_id) = self.module_scope()?;
|
let (def_map, local_id) = self.module_scope()?;
|
||||||
Some(ModuleId { krate: def_map.krate, local_id })
|
Some(ModuleId { krate: def_map.krate, local_id })
|
||||||
|
|
|
@ -362,6 +362,26 @@ fn main() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_local_macro() {
|
||||||
|
assert_snapshot!(
|
||||||
|
infer(r#"
|
||||||
|
fn main() {
|
||||||
|
macro_rules! foo {
|
||||||
|
() => { 1usize }
|
||||||
|
}
|
||||||
|
let _a = foo!();
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
![0; 6) '1usize': usize
|
||||||
|
[11; 90) '{ ...!(); }': ()
|
||||||
|
[17; 66) 'macro_... }': {unknown}
|
||||||
|
[75; 77) '_a': usize
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_builtin_macros_line() {
|
fn infer_builtin_macros_line() {
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
|
|
|
@ -787,6 +787,21 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_in_local_macro() {
|
||||||
|
check_goto(
|
||||||
|
"
|
||||||
|
//- /lib.rs
|
||||||
|
fn bar() {
|
||||||
|
macro_rules! foo { () => { () } }
|
||||||
|
<|>foo!();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
"foo MACRO_CALL FileId(1) [15; 48) [28; 31)",
|
||||||
|
"macro_rules! foo { () => { () } }|foo",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_def_for_field_init_shorthand() {
|
fn goto_def_for_field_init_shorthand() {
|
||||||
covers!(ra_ide_db::goto_def_for_field_init_shorthand);
|
covers!(ra_ide_db::goto_def_for_field_init_shorthand);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue