mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 21:54:42 +00:00
Merge #1208
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:
commit
aa7bdfd37f
9 changed files with 97 additions and 6 deletions
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"] ] ),
|
||||||
|
|
Loading…
Reference in a new issue