1208: [WIP] Goto for Macro's r=matklad a=Lapz

Adds goto definition for macros. Currently only works for macros in the current crate ~~otherwise it panics~~. Proper macro resolution needs to be added for it to resolve macros in other crates.

Todo
- [X] Allow goto from macro calls
- [X] Fix panics
- [x] Add tests



![Screen Recording 2019-04-25 at 18 00 24](https://user-images.githubusercontent.com/19998186/56754499-1dd01c00-6785-11e9-9e9a-1e36de70cfa3.gif)



Co-authored-by: Lenard Pratt <l3np27@gmail.com>
This commit is contained in:
bors[bot] 2019-05-04 18:38:10 +00:00
commit aa7bdfd37f
9 changed files with 97 additions and 6 deletions

View file

@ -69,7 +69,7 @@ pub use self::{
expr::ExprScopes, expr::ExprScopes,
resolve::Resolution, resolve::Resolution,
generics::{GenericParams, GenericParam, HasGenericParams}, generics::{GenericParams, GenericParam, HasGenericParams},
source_binder::{SourceAnalyzer, PathResolution, ScopeEntryWithSyntax}, source_binder::{SourceAnalyzer, PathResolution, ScopeEntryWithSyntax,MacroByExampleDef},
}; };
pub use self::code_model_api::{ pub use self::code_model_api::{
@ -80,5 +80,5 @@ pub use self::code_model_api::{
Function, FnSignature, Function, FnSignature,
StructField, FieldSource, StructField, FieldSource,
Static, Const, ConstSignature, Static, Const, ConstSignature,
Trait, TypeAlias, Container, Trait, TypeAlias, Container
}; };

View file

@ -10,7 +10,7 @@ use std::sync::Arc;
use rustc_hash::{FxHashSet, FxHashMap}; use rustc_hash::{FxHashSet, FxHashMap};
use ra_db::{FileId, FilePosition}; use ra_db::{FileId, FilePosition};
use ra_syntax::{ use ra_syntax::{
SyntaxNode, AstPtr, TextUnit, SyntaxNodePtr, TextRange, SyntaxNode, AstPtr, TextUnit, SyntaxNodePtr, TextRange,TreeArc,
ast::{self, AstNode, NameOwner}, ast::{self, AstNode, NameOwner},
algo::find_node_at_offset, algo::find_node_at_offset,
SyntaxKind::*, SyntaxKind::*,
@ -18,9 +18,10 @@ use ra_syntax::{
use crate::{ use crate::{
HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name, HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name,
AsName, Module, HirFileId, Crate, Trait, Resolver, Ty, AsName, Module, HirFileId, Crate, Trait, Resolver, Ty,Path,
expr::{BodySourceMap, scope::{ScopeId, ExprScopes}}, expr::{BodySourceMap, scope::{ScopeId, ExprScopes}},
ids::LocationCtx, ids::{LocationCtx,MacroCallId},
docs::{docs_from_ast,Documentation},
expr, AstId, expr, AstId,
}; };
@ -184,9 +185,28 @@ pub enum PathResolution {
/// A generic parameter /// A generic parameter
GenericParam(u32), GenericParam(u32),
SelfType(crate::ImplBlock), SelfType(crate::ImplBlock),
Macro(MacroByExampleDef),
AssocItem(crate::ImplItem), AssocItem(crate::ImplItem),
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MacroByExampleDef {
pub(crate) id: MacroCallId,
}
impl MacroByExampleDef {
pub fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<ast::MacroCall>) {
let loc = self.id.loc(db);
(self.id.into(), loc.def.0.to_node(db))
}
}
impl crate::Docs for MacroByExampleDef {
fn docs(&self, db: &impl HirDatabase) -> Option<Documentation> {
docs_from_ast(&*self.source(db).1)
}
}
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScopeEntryWithSyntax { pub struct ScopeEntryWithSyntax {
pub(crate) name: Name, pub(crate) name: Name,
@ -264,6 +284,23 @@ impl SourceAnalyzer {
self.infer.as_ref()?.field_resolution(expr_id) self.infer.as_ref()?.field_resolution(expr_id)
} }
pub fn resolve_macro_call(
&self,
db: &impl HirDatabase,
file_id: FileId,
macro_call: &ast::MacroCall,
) -> Option<MacroByExampleDef> {
let hir_id = file_id.into();
let ast_id = db.ast_id_map(hir_id).ast_id(macro_call).with_file_id(hir_id);
let call_id = self.resolver.resolve_macro_call(
db,
macro_call.path().and_then(Path::from_ast),
ast_id,
);
call_id.map(|id| MacroByExampleDef { id })
}
pub fn resolve_hir_path( pub fn resolve_hir_path(
&self, &self,
db: &impl HirDatabase, db: &impl HirDatabase,

View file

@ -89,6 +89,7 @@ pub enum CompletionItemKind {
TypeAlias, TypeAlias,
Method, Method,
TypeParam, TypeParam,
Macro,
} }
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]

View file

@ -213,6 +213,15 @@ impl NavigationTarget {
} }
} }
pub(crate) fn from_macro_def(
db: &RootDatabase,
macro_call: hir::MacroByExampleDef,
) -> NavigationTarget {
let (file_id, node) = macro_call.source(db);
log::debug!("nav target {}", node.syntax().debug_dump());
NavigationTarget::from_named(file_id.original_file(db), &*node)
}
#[cfg(test)] #[cfg(test)]
pub(crate) fn assert_match(&self, expected: &str) { pub(crate) fn assert_match(&self, expected: &str) {
let actual = self.debug_render(); let actual = self.debug_render();
@ -289,6 +298,7 @@ impl NavigationTarget {
.visit(doc_comments::<ast::StaticDef>) .visit(doc_comments::<ast::StaticDef>)
.visit(doc_comments::<ast::NamedFieldDef>) .visit(doc_comments::<ast::NamedFieldDef>)
.visit(doc_comments::<ast::EnumVariant>) .visit(doc_comments::<ast::EnumVariant>)
.visit(doc_comments::<ast::MacroCall>)
.accept(&node)? .accept(&node)?
} }

View file

@ -59,6 +59,21 @@ pub(crate) fn reference_definition(
return Exact(NavigationTarget::from_function(db, func)); return Exact(NavigationTarget::from_function(db, func));
} }
} }
//it could be a macro call
if let Some(macro_call) = name_ref
.syntax()
.parent()
.and_then(|node| node.parent())
.and_then(|node| node.parent())
.and_then(ast::MacroCall::cast)
{
tested_by!(goto_definition_works_for_macros);
if let Some(macro_call) = analyzer.resolve_macro_call(db, file_id, macro_call) {
return Exact(NavigationTarget::from_macro_def(db, macro_call));
}
}
// It could also be a field access // It could also be a field access
if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) {
tested_by!(goto_definition_works_for_fields); tested_by!(goto_definition_works_for_fields);
@ -97,6 +112,10 @@ pub(crate) fn reference_definition(
hir::PathResolution::GenericParam(..) => { hir::PathResolution::GenericParam(..) => {
// FIXME: go to the generic param def // FIXME: go to the generic param def
} }
hir::PathResolution::Macro(def) => {
let nav = NavigationTarget::from_macro_def(db, def);
return Exact(nav);
}
hir::PathResolution::SelfType(impl_block) => { hir::PathResolution::SelfType(impl_block) => {
let ty = impl_block.target_ty(db); let ty = impl_block.target_ty(db);
@ -156,6 +175,7 @@ fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget>
.visit(|node: &ast::TraitDef| NavigationTarget::from_named(file_id, node)) .visit(|node: &ast::TraitDef| NavigationTarget::from_named(file_id, node))
.visit(|node: &ast::NamedFieldDef| NavigationTarget::from_named(file_id, node)) .visit(|node: &ast::NamedFieldDef| NavigationTarget::from_named(file_id, node))
.visit(|node: &ast::Module| NavigationTarget::from_named(file_id, node)) .visit(|node: &ast::Module| NavigationTarget::from_named(file_id, node))
.visit(|node: &ast::MacroCall| NavigationTarget::from_named(file_id, node))
.accept(node) .accept(node)
} }
@ -227,6 +247,26 @@ mod tests {
); );
} }
#[test]
fn goto_definition_works_for_macros() {
covers!(goto_definition_works_for_macros);
check_goto(
"
//- /lib.rs
macro_rules! foo {
() => {
{}
};
}
fn bar() {
<|>foo!();
}
",
"foo MACRO_CALL FileId(1) [0; 50) [13; 16)",
);
}
#[test] #[test]
fn goto_definition_works_for_methods() { fn goto_definition_works_for_methods() {
covers!(goto_definition_works_for_methods); covers!(goto_definition_works_for_methods);

View file

@ -1,5 +1,6 @@
test_utils::marks!( test_utils::marks!(
inserts_parens_for_function_calls inserts_parens_for_function_calls
goto_definition_works_for_macros
goto_definition_works_for_methods goto_definition_works_for_methods
goto_definition_works_for_fields goto_definition_works_for_fields
goto_definition_works_for_named_fields goto_definition_works_for_named_fields

View file

@ -73,6 +73,7 @@ impl Conv for CompletionItemKind {
CompletionItemKind::Static => Value, CompletionItemKind::Static => Value,
CompletionItemKind::Method => Method, CompletionItemKind::Method => Method,
CompletionItemKind::TypeParam => TypeParameter, CompletionItemKind::TypeParam => TypeParameter,
CompletionItemKind::Macro => Method,
} }
} }
} }

View file

@ -1761,6 +1761,7 @@ impl ToOwned for MacroCall {
impl ast::NameOwner for MacroCall {} impl ast::NameOwner for MacroCall {}
impl ast::AttrsOwner for MacroCall {} impl ast::AttrsOwner for MacroCall {}
impl ast::DocCommentsOwner for MacroCall {}
impl MacroCall { impl MacroCall {
pub fn token_tree(&self) -> Option<&TokenTree> { pub fn token_tree(&self) -> Option<&TokenTree> {
super::child_opt(self) super::child_opt(self)

View file

@ -552,7 +552,7 @@ Grammar(
"Name": (), "Name": (),
"NameRef": (), "NameRef": (),
"MacroCall": ( "MacroCall": (
traits: [ "NameOwner", "AttrsOwner" ], traits: [ "NameOwner", "AttrsOwner","DocCommentsOwner" ],
options: [ "TokenTree", "Path" ], options: [ "TokenTree", "Path" ],
), ),
"Attr": ( options: [ ["value", "TokenTree"] ] ), "Attr": ( options: [ ["value", "TokenTree"] ] ),