mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 13:03:31 +00:00
Merge #1960
1960: Replace AST visitors with macro r=viorina a=viorina Fixes #1672. Co-authored-by: Ekaterina Babshukova <ekaterina.babshukova@yandex.ru>
This commit is contained in:
commit
d3872964f8
15 changed files with 349 additions and 429 deletions
|
@ -51,13 +51,13 @@ mod tests {
|
|||
fn split_import_works_with_trees() {
|
||||
check_assist(
|
||||
split_import,
|
||||
"use algo:<|>:visitor::{Visitor, visit}",
|
||||
"use algo::{<|>visitor::{Visitor, visit}}",
|
||||
"use crate:<|>:db::{RootDatabase, FileSymbol}",
|
||||
"use crate::{<|>db::{RootDatabase, FileSymbol}}",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn split_import_target() {
|
||||
check_assist_target(split_import, "use algo::<|>visitor::{Visitor, visit}", "::");
|
||||
check_assist_target(split_import, "use crate::<|>db::{RootDatabase, FileSymbol}", "::");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use ra_syntax::{
|
||||
algo::visit::{visitor_ctx, VisitorCtx},
|
||||
ast, AstNode,
|
||||
};
|
||||
use ra_syntax::{ast, match_ast, AstNode};
|
||||
use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
|
||||
|
@ -19,10 +16,13 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
|
|||
|
||||
let mut params = FxHashMap::default();
|
||||
for node in ctx.token.parent().ancestors() {
|
||||
let _ = visitor_ctx(&mut params)
|
||||
.visit::<ast::SourceFile, _>(process)
|
||||
.visit::<ast::ItemList, _>(process)
|
||||
.accept(&node);
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::SourceFile(it) => { process(it, &mut params) },
|
||||
ast::ItemList(it) => { process(it, &mut params) },
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
params
|
||||
.into_iter()
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use ra_syntax::{
|
||||
algo::visit::{visitor, Visitor},
|
||||
ast::{self, LoopBodyOwner},
|
||||
AstNode,
|
||||
match_ast, AstNode,
|
||||
SyntaxKind::*,
|
||||
SyntaxToken,
|
||||
};
|
||||
|
@ -84,12 +83,15 @@ fn is_in_loop_body(leaf: &SyntaxToken) -> bool {
|
|||
if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
|
||||
break;
|
||||
}
|
||||
let loop_body = visitor()
|
||||
.visit::<ast::ForExpr, _>(|it| it.loop_body())
|
||||
.visit::<ast::WhileExpr, _>(|it| it.loop_body())
|
||||
.visit::<ast::LoopExpr, _>(|it| it.loop_body())
|
||||
.accept(&node);
|
||||
if let Some(Some(body)) = loop_body {
|
||||
let loop_body = match_ast! {
|
||||
match node {
|
||||
ast::ForExpr(it) => { it.loop_body() },
|
||||
ast::WhileExpr(it) => { it.loop_body() },
|
||||
ast::LoopExpr(it) => { it.loop_body() },
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
if let Some(body) = loop_body {
|
||||
if leaf.text_range().is_subrange(&body.syntax().text_range()) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
use hir::{AssocItem, FieldSource, HasSource, ModuleSource};
|
||||
use ra_db::{FileId, SourceDatabase};
|
||||
use ra_syntax::{
|
||||
algo::visit::{visitor, Visitor},
|
||||
ast::{self, DocCommentsOwner},
|
||||
AstNode, AstPtr, SmolStr,
|
||||
match_ast, AstNode, AstPtr, SmolStr,
|
||||
SyntaxKind::{self, NAME},
|
||||
SyntaxNode, TextRange,
|
||||
};
|
||||
|
@ -308,19 +307,22 @@ pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option
|
|||
let parse = db.parse(symbol.file_id);
|
||||
let node = symbol.ptr.to_node(parse.tree().syntax());
|
||||
|
||||
visitor()
|
||||
.visit(|it: ast::FnDef| it.doc_comment_text())
|
||||
.visit(|it: ast::StructDef| it.doc_comment_text())
|
||||
.visit(|it: ast::EnumDef| it.doc_comment_text())
|
||||
.visit(|it: ast::TraitDef| it.doc_comment_text())
|
||||
.visit(|it: ast::Module| it.doc_comment_text())
|
||||
.visit(|it: ast::TypeAliasDef| it.doc_comment_text())
|
||||
.visit(|it: ast::ConstDef| it.doc_comment_text())
|
||||
.visit(|it: ast::StaticDef| it.doc_comment_text())
|
||||
.visit(|it: ast::RecordFieldDef| it.doc_comment_text())
|
||||
.visit(|it: ast::EnumVariant| it.doc_comment_text())
|
||||
.visit(|it: ast::MacroCall| it.doc_comment_text())
|
||||
.accept(&node)?
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::FnDef(it) => { it.doc_comment_text() },
|
||||
ast::StructDef(it) => { it.doc_comment_text() },
|
||||
ast::EnumDef(it) => { it.doc_comment_text() },
|
||||
ast::TraitDef(it) => { it.doc_comment_text() },
|
||||
ast::Module(it) => { it.doc_comment_text() },
|
||||
ast::TypeAliasDef(it) => { it.doc_comment_text() },
|
||||
ast::ConstDef(it) => { it.doc_comment_text() },
|
||||
ast::StaticDef(it) => { it.doc_comment_text() },
|
||||
ast::RecordFieldDef(it) => { it.doc_comment_text() },
|
||||
ast::EnumVariant(it) => { it.doc_comment_text() },
|
||||
ast::MacroCall(it) => { it.doc_comment_text() },
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a description of a symbol.
|
||||
|
@ -330,16 +332,19 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) ->
|
|||
let parse = db.parse(symbol.file_id);
|
||||
let node = symbol.ptr.to_node(parse.tree().syntax());
|
||||
|
||||
visitor()
|
||||
.visit(|node: ast::FnDef| node.short_label())
|
||||
.visit(|node: ast::StructDef| node.short_label())
|
||||
.visit(|node: ast::EnumDef| node.short_label())
|
||||
.visit(|node: ast::TraitDef| node.short_label())
|
||||
.visit(|node: ast::Module| node.short_label())
|
||||
.visit(|node: ast::TypeAliasDef| node.short_label())
|
||||
.visit(|node: ast::ConstDef| node.short_label())
|
||||
.visit(|node: ast::StaticDef| node.short_label())
|
||||
.visit(|node: ast::RecordFieldDef| node.short_label())
|
||||
.visit(|node: ast::EnumVariant| node.short_label())
|
||||
.accept(&node)?
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::FnDef(it) => { it.short_label() },
|
||||
ast::StructDef(it) => { it.short_label() },
|
||||
ast::EnumDef(it) => { it.short_label() },
|
||||
ast::TraitDef(it) => { it.short_label() },
|
||||
ast::Module(it) => { it.short_label() },
|
||||
ast::TypeAliasDef(it) => { it.short_label() },
|
||||
ast::ConstDef(it) => { it.short_label() },
|
||||
ast::StaticDef(it) => { it.short_label() },
|
||||
ast::RecordFieldDef(it) => { it.short_label() },
|
||||
ast::EnumVariant(it) => { it.short_label() },
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
use crate::TextRange;
|
||||
|
||||
use ra_syntax::{
|
||||
algo::visit::{visitor, Visitor},
|
||||
ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner},
|
||||
AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent,
|
||||
match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -101,63 +100,66 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
|
|||
})
|
||||
}
|
||||
|
||||
visitor()
|
||||
.visit(|fn_def: ast::FnDef| {
|
||||
let mut detail = String::from("fn");
|
||||
if let Some(type_param_list) = fn_def.type_param_list() {
|
||||
collapse_ws(type_param_list.syntax(), &mut detail);
|
||||
}
|
||||
if let Some(param_list) = fn_def.param_list() {
|
||||
collapse_ws(param_list.syntax(), &mut detail);
|
||||
}
|
||||
if let Some(ret_type) = fn_def.ret_type() {
|
||||
detail.push_str(" ");
|
||||
collapse_ws(ret_type.syntax(), &mut detail);
|
||||
}
|
||||
|
||||
decl_with_detail(fn_def, Some(detail))
|
||||
})
|
||||
.visit(decl::<ast::StructDef>)
|
||||
.visit(decl::<ast::EnumDef>)
|
||||
.visit(decl::<ast::EnumVariant>)
|
||||
.visit(decl::<ast::TraitDef>)
|
||||
.visit(decl::<ast::Module>)
|
||||
.visit(|td: ast::TypeAliasDef| {
|
||||
let ty = td.type_ref();
|
||||
decl_with_type_ref(td, ty)
|
||||
})
|
||||
.visit(decl_with_ascription::<ast::RecordFieldDef>)
|
||||
.visit(decl_with_ascription::<ast::ConstDef>)
|
||||
.visit(decl_with_ascription::<ast::StaticDef>)
|
||||
.visit(|im: ast::ImplBlock| {
|
||||
let target_type = im.target_type()?;
|
||||
let target_trait = im.target_trait();
|
||||
let label = match target_trait {
|
||||
None => format!("impl {}", target_type.syntax().text()),
|
||||
Some(t) => {
|
||||
format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),)
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::FnDef(it) => {
|
||||
let mut detail = String::from("fn");
|
||||
if let Some(type_param_list) = it.type_param_list() {
|
||||
collapse_ws(type_param_list.syntax(), &mut detail);
|
||||
}
|
||||
if let Some(param_list) = it.param_list() {
|
||||
collapse_ws(param_list.syntax(), &mut detail);
|
||||
}
|
||||
if let Some(ret_type) = it.ret_type() {
|
||||
detail.push_str(" ");
|
||||
collapse_ws(ret_type.syntax(), &mut detail);
|
||||
}
|
||||
};
|
||||
|
||||
let node = StructureNode {
|
||||
parent: None,
|
||||
label,
|
||||
navigation_range: target_type.syntax().text_range(),
|
||||
node_range: im.syntax().text_range(),
|
||||
kind: im.syntax().kind(),
|
||||
detail: None,
|
||||
deprecated: false,
|
||||
};
|
||||
Some(node)
|
||||
})
|
||||
.visit(|mc: ast::MacroCall| {
|
||||
let first_token = mc.syntax().first_token().unwrap();
|
||||
if first_token.text().as_str() != "macro_rules" {
|
||||
return None;
|
||||
}
|
||||
decl(mc)
|
||||
})
|
||||
.accept(&node)?
|
||||
decl_with_detail(it, Some(detail))
|
||||
},
|
||||
ast::StructDef(it) => { decl(it) },
|
||||
ast::EnumDef(it) => { decl(it) },
|
||||
ast::EnumVariant(it) => { decl(it) },
|
||||
ast::TraitDef(it) => { decl(it) },
|
||||
ast::Module(it) => { decl(it) },
|
||||
ast::TypeAliasDef(it) => {
|
||||
let ty = it.type_ref();
|
||||
decl_with_type_ref(it, ty)
|
||||
},
|
||||
ast::RecordFieldDef(it) => { decl_with_ascription(it) },
|
||||
ast::ConstDef(it) => { decl_with_ascription(it) },
|
||||
ast::StaticDef(it) => { decl_with_ascription(it) },
|
||||
ast::ImplBlock(it) => {
|
||||
let target_type = it.target_type()?;
|
||||
let target_trait = it.target_trait();
|
||||
let label = match target_trait {
|
||||
None => format!("impl {}", target_type.syntax().text()),
|
||||
Some(t) => {
|
||||
format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),)
|
||||
}
|
||||
};
|
||||
|
||||
let node = StructureNode {
|
||||
parent: None,
|
||||
label,
|
||||
navigation_range: target_type.syntax().text_range(),
|
||||
node_range: it.syntax().text_range(),
|
||||
kind: it.syntax().kind(),
|
||||
detail: None,
|
||||
deprecated: false,
|
||||
};
|
||||
Some(node)
|
||||
},
|
||||
ast::MacroCall(it) => {
|
||||
let first_token = it.syntax().first_token().unwrap();
|
||||
if first_token.text().as_str() != "macro_rules" {
|
||||
return None;
|
||||
}
|
||||
decl(it)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -2,12 +2,9 @@
|
|||
|
||||
use ra_db::{FileId, SourceDatabase};
|
||||
use ra_syntax::{
|
||||
algo::{
|
||||
find_node_at_offset,
|
||||
visit::{visitor, Visitor},
|
||||
},
|
||||
algo::find_node_at_offset,
|
||||
ast::{self, DocCommentsOwner},
|
||||
AstNode, SyntaxNode,
|
||||
match_ast, AstNode, SyntaxNode,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -114,91 +111,99 @@ pub(crate) fn name_definition(
|
|||
}
|
||||
|
||||
fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> {
|
||||
visitor()
|
||||
.visit(|node: ast::StructDef| {
|
||||
NavigationTarget::from_named(
|
||||
file_id,
|
||||
&node,
|
||||
node.doc_comment_text(),
|
||||
node.short_label(),
|
||||
)
|
||||
})
|
||||
.visit(|node: ast::EnumDef| {
|
||||
NavigationTarget::from_named(
|
||||
file_id,
|
||||
&node,
|
||||
node.doc_comment_text(),
|
||||
node.short_label(),
|
||||
)
|
||||
})
|
||||
.visit(|node: ast::EnumVariant| {
|
||||
NavigationTarget::from_named(
|
||||
file_id,
|
||||
&node,
|
||||
node.doc_comment_text(),
|
||||
node.short_label(),
|
||||
)
|
||||
})
|
||||
.visit(|node: ast::FnDef| {
|
||||
NavigationTarget::from_named(
|
||||
file_id,
|
||||
&node,
|
||||
node.doc_comment_text(),
|
||||
node.short_label(),
|
||||
)
|
||||
})
|
||||
.visit(|node: ast::TypeAliasDef| {
|
||||
NavigationTarget::from_named(
|
||||
file_id,
|
||||
&node,
|
||||
node.doc_comment_text(),
|
||||
node.short_label(),
|
||||
)
|
||||
})
|
||||
.visit(|node: ast::ConstDef| {
|
||||
NavigationTarget::from_named(
|
||||
file_id,
|
||||
&node,
|
||||
node.doc_comment_text(),
|
||||
node.short_label(),
|
||||
)
|
||||
})
|
||||
.visit(|node: ast::StaticDef| {
|
||||
NavigationTarget::from_named(
|
||||
file_id,
|
||||
&node,
|
||||
node.doc_comment_text(),
|
||||
node.short_label(),
|
||||
)
|
||||
})
|
||||
.visit(|node: ast::TraitDef| {
|
||||
NavigationTarget::from_named(
|
||||
file_id,
|
||||
&node,
|
||||
node.doc_comment_text(),
|
||||
node.short_label(),
|
||||
)
|
||||
})
|
||||
.visit(|node: ast::RecordFieldDef| {
|
||||
NavigationTarget::from_named(
|
||||
file_id,
|
||||
&node,
|
||||
node.doc_comment_text(),
|
||||
node.short_label(),
|
||||
)
|
||||
})
|
||||
.visit(|node: ast::Module| {
|
||||
NavigationTarget::from_named(
|
||||
file_id,
|
||||
&node,
|
||||
node.doc_comment_text(),
|
||||
node.short_label(),
|
||||
)
|
||||
})
|
||||
.visit(|node: ast::MacroCall| {
|
||||
NavigationTarget::from_named(file_id, &node, node.doc_comment_text(), None)
|
||||
})
|
||||
.accept(node)
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::StructDef(it) => {
|
||||
Some(NavigationTarget::from_named(
|
||||
file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
it.short_label(),
|
||||
))
|
||||
},
|
||||
ast::EnumDef(it) => {
|
||||
Some(NavigationTarget::from_named(
|
||||
file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
it.short_label(),
|
||||
))
|
||||
},
|
||||
ast::EnumVariant(it) => {
|
||||
Some(NavigationTarget::from_named(
|
||||
file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
it.short_label(),
|
||||
))
|
||||
},
|
||||
ast::FnDef(it) => {
|
||||
Some(NavigationTarget::from_named(
|
||||
file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
it.short_label(),
|
||||
))
|
||||
},
|
||||
ast::TypeAliasDef(it) => {
|
||||
Some(NavigationTarget::from_named(
|
||||
file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
it.short_label(),
|
||||
))
|
||||
},
|
||||
ast::ConstDef(it) => {
|
||||
Some(NavigationTarget::from_named(
|
||||
file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
it.short_label(),
|
||||
))
|
||||
},
|
||||
ast::StaticDef(it) => {
|
||||
Some(NavigationTarget::from_named(
|
||||
file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
it.short_label(),
|
||||
))
|
||||
},
|
||||
ast::TraitDef(it) => {
|
||||
Some(NavigationTarget::from_named(
|
||||
file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
it.short_label(),
|
||||
))
|
||||
},
|
||||
ast::RecordFieldDef(it) => {
|
||||
Some(NavigationTarget::from_named(
|
||||
file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
it.short_label(),
|
||||
))
|
||||
},
|
||||
ast::Module(it) => {
|
||||
Some(NavigationTarget::from_named(
|
||||
file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
it.short_label(),
|
||||
))
|
||||
},
|
||||
ast::MacroCall(it) => {
|
||||
Some(NavigationTarget::from_named(
|
||||
file_id,
|
||||
&it,
|
||||
it.doc_comment_text(),
|
||||
None,
|
||||
))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -3,12 +3,9 @@
|
|||
use hir::{Adt, HasSource, HirDisplay};
|
||||
use ra_db::SourceDatabase;
|
||||
use ra_syntax::{
|
||||
algo::{
|
||||
ancestors_at_offset, find_covering_element, find_node_at_offset,
|
||||
visit::{visitor, Visitor},
|
||||
},
|
||||
algo::{ancestors_at_offset, find_covering_element, find_node_at_offset},
|
||||
ast::{self, DocCommentsOwner},
|
||||
AstNode,
|
||||
match_ast, AstNode,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -178,37 +175,45 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
|
|||
}
|
||||
} else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
|
||||
if let Some(parent) = name.syntax().parent() {
|
||||
let text = visitor()
|
||||
.visit(|node: ast::StructDef| {
|
||||
hover_text(node.doc_comment_text(), node.short_label())
|
||||
})
|
||||
.visit(|node: ast::EnumDef| hover_text(node.doc_comment_text(), node.short_label()))
|
||||
.visit(|node: ast::EnumVariant| {
|
||||
hover_text(node.doc_comment_text(), node.short_label())
|
||||
})
|
||||
.visit(|node: ast::FnDef| hover_text(node.doc_comment_text(), node.short_label()))
|
||||
.visit(|node: ast::TypeAliasDef| {
|
||||
hover_text(node.doc_comment_text(), node.short_label())
|
||||
})
|
||||
.visit(|node: ast::ConstDef| {
|
||||
hover_text(node.doc_comment_text(), node.short_label())
|
||||
})
|
||||
.visit(|node: ast::StaticDef| {
|
||||
hover_text(node.doc_comment_text(), node.short_label())
|
||||
})
|
||||
.visit(|node: ast::TraitDef| {
|
||||
hover_text(node.doc_comment_text(), node.short_label())
|
||||
})
|
||||
.visit(|node: ast::RecordFieldDef| {
|
||||
hover_text(node.doc_comment_text(), node.short_label())
|
||||
})
|
||||
.visit(|node: ast::Module| hover_text(node.doc_comment_text(), node.short_label()))
|
||||
.visit(|node: ast::MacroCall| hover_text(node.doc_comment_text(), None))
|
||||
.accept(&parent);
|
||||
|
||||
if let Some(text) = text {
|
||||
res.extend(text);
|
||||
}
|
||||
let text = match_ast! {
|
||||
match parent {
|
||||
ast::StructDef(it) => {
|
||||
hover_text(it.doc_comment_text(), it.short_label())
|
||||
},
|
||||
ast::EnumDef(it) => {
|
||||
hover_text(it.doc_comment_text(), it.short_label())
|
||||
},
|
||||
ast::EnumVariant(it) => {
|
||||
hover_text(it.doc_comment_text(), it.short_label())
|
||||
},
|
||||
ast::FnDef(it) => {
|
||||
hover_text(it.doc_comment_text(), it.short_label())
|
||||
},
|
||||
ast::TypeAliasDef(it) => {
|
||||
hover_text(it.doc_comment_text(), it.short_label())
|
||||
},
|
||||
ast::ConstDef(it) => {
|
||||
hover_text(it.doc_comment_text(), it.short_label())
|
||||
},
|
||||
ast::StaticDef(it) => {
|
||||
hover_text(it.doc_comment_text(), it.short_label())
|
||||
},
|
||||
ast::TraitDef(it) => {
|
||||
hover_text(it.doc_comment_text(), it.short_label())
|
||||
},
|
||||
ast::RecordFieldDef(it) => {
|
||||
hover_text(it.doc_comment_text(), it.short_label())
|
||||
},
|
||||
ast::Module(it) => {
|
||||
hover_text(it.doc_comment_text(), it.short_label())
|
||||
},
|
||||
ast::MacroCall(it) => {
|
||||
hover_text(it.doc_comment_text(), None)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
res.extend(text);
|
||||
}
|
||||
|
||||
if !res.is_empty() && range.is_none() {
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
use crate::{db::RootDatabase, FileId};
|
||||
use hir::{HirDisplay, SourceAnalyzer, Ty};
|
||||
use ra_syntax::{
|
||||
algo::visit::{visitor, Visitor},
|
||||
ast::{self, AstNode, TypeAscriptionOwner},
|
||||
SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange,
|
||||
match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
@ -33,55 +32,58 @@ fn get_inlay_hints(
|
|||
file_id: FileId,
|
||||
node: &SyntaxNode,
|
||||
) -> Option<Vec<InlayHint>> {
|
||||
visitor()
|
||||
.visit(|let_statement: ast::LetStmt| {
|
||||
if let_statement.ascribed_type().is_some() {
|
||||
return None;
|
||||
}
|
||||
let pat = let_statement.pat()?;
|
||||
let analyzer = SourceAnalyzer::new(db, file_id, let_statement.syntax(), None);
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, false))
|
||||
})
|
||||
.visit(|closure_parameter: ast::LambdaExpr| {
|
||||
let analyzer = SourceAnalyzer::new(db, file_id, closure_parameter.syntax(), None);
|
||||
closure_parameter.param_list().map(|param_list| {
|
||||
param_list
|
||||
.params()
|
||||
.filter(|closure_param| closure_param.ascribed_type().is_none())
|
||||
.filter_map(|closure_param| closure_param.pat())
|
||||
.map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, false))
|
||||
.flatten()
|
||||
.collect()
|
||||
})
|
||||
})
|
||||
.visit(|for_expression: ast::ForExpr| {
|
||||
let pat = for_expression.pat()?;
|
||||
let analyzer = SourceAnalyzer::new(db, file_id, for_expression.syntax(), None);
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, false))
|
||||
})
|
||||
.visit(|if_expr: ast::IfExpr| {
|
||||
let pat = if_expr.condition()?.pat()?;
|
||||
let analyzer = SourceAnalyzer::new(db, file_id, if_expr.syntax(), None);
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, true))
|
||||
})
|
||||
.visit(|while_expr: ast::WhileExpr| {
|
||||
let pat = while_expr.condition()?.pat()?;
|
||||
let analyzer = SourceAnalyzer::new(db, file_id, while_expr.syntax(), None);
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, true))
|
||||
})
|
||||
.visit(|match_arm_list: ast::MatchArmList| {
|
||||
let analyzer = SourceAnalyzer::new(db, file_id, match_arm_list.syntax(), None);
|
||||
Some(
|
||||
match_arm_list
|
||||
.arms()
|
||||
.map(|match_arm| match_arm.pats())
|
||||
.flatten()
|
||||
.map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, true))
|
||||
.flatten()
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.accept(&node)?
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::LetStmt(it) => {
|
||||
if it.ascribed_type().is_some() {
|
||||
return None;
|
||||
}
|
||||
let pat = it.pat()?;
|
||||
let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None);
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, false))
|
||||
},
|
||||
ast::LambdaExpr(it) => {
|
||||
let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None);
|
||||
it.param_list().map(|param_list| {
|
||||
param_list
|
||||
.params()
|
||||
.filter(|closure_param| closure_param.ascribed_type().is_none())
|
||||
.filter_map(|closure_param| closure_param.pat())
|
||||
.map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, false))
|
||||
.flatten()
|
||||
.collect()
|
||||
})
|
||||
},
|
||||
ast::ForExpr(it) => {
|
||||
let pat = it.pat()?;
|
||||
let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None);
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, false))
|
||||
},
|
||||
ast::IfExpr(it) => {
|
||||
let pat = it.condition()?.pat()?;
|
||||
let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None);
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, true))
|
||||
},
|
||||
ast::WhileExpr(it) => {
|
||||
let pat = it.condition()?.pat()?;
|
||||
let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None);
|
||||
Some(get_pat_type_hints(db, &analyzer, pat, true))
|
||||
},
|
||||
ast::MatchArmList(it) => {
|
||||
let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None);
|
||||
Some(
|
||||
it
|
||||
.arms()
|
||||
.map(|match_arm| match_arm.pats())
|
||||
.flatten()
|
||||
.map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, true))
|
||||
.flatten()
|
||||
.collect(),
|
||||
)
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_pat_type_hints(
|
||||
|
|
|
@ -32,9 +32,8 @@ use ra_db::{
|
|||
SourceDatabase, SourceRootId,
|
||||
};
|
||||
use ra_syntax::{
|
||||
algo::visit::{visitor, Visitor},
|
||||
ast::{self, NameOwner},
|
||||
AstNode, Parse, SmolStr, SourceFile,
|
||||
match_ast, AstNode, Parse, SmolStr, SourceFile,
|
||||
SyntaxKind::{self, *},
|
||||
SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent,
|
||||
};
|
||||
|
@ -306,16 +305,19 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
|
|||
|
||||
Some((name, ptr, name_range))
|
||||
}
|
||||
visitor()
|
||||
.visit(decl::<ast::FnDef>)
|
||||
.visit(decl::<ast::StructDef>)
|
||||
.visit(decl::<ast::EnumDef>)
|
||||
.visit(decl::<ast::TraitDef>)
|
||||
.visit(decl::<ast::Module>)
|
||||
.visit(decl::<ast::TypeAliasDef>)
|
||||
.visit(decl::<ast::ConstDef>)
|
||||
.visit(decl::<ast::StaticDef>)
|
||||
.accept(node)?
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::FnDef(it) => { decl(it) },
|
||||
ast::StructDef(it) => { decl(it) },
|
||||
ast::EnumDef(it) => { decl(it) },
|
||||
ast::TraitDef(it) => { decl(it) },
|
||||
ast::Module(it) => { decl(it) },
|
||||
ast::TypeAliasDef(it) => { decl(it) },
|
||||
ast::ConstDef(it) => { decl(it) },
|
||||
ast::StaticDef(it) => { decl(it) },
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
pub mod visit;
|
||||
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
//! FIXME: write short doc here
|
||||
|
||||
use crate::{AstNode, SyntaxNode};
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub fn visitor<'a, T>() -> impl Visitor<'a, Output = T> {
|
||||
EmptyVisitor { ph: PhantomData }
|
||||
}
|
||||
|
||||
pub fn visitor_ctx<'a, T, C>(ctx: C) -> impl VisitorCtx<'a, Output = T, Ctx = C> {
|
||||
EmptyVisitorCtx { ph: PhantomData, ctx }
|
||||
}
|
||||
|
||||
pub trait Visitor<'a>: Sized {
|
||||
type Output;
|
||||
fn accept(self, node: &'a SyntaxNode) -> Option<Self::Output>;
|
||||
fn visit<N, F>(self, f: F) -> Vis<Self, N, F>
|
||||
where
|
||||
N: AstNode + 'a,
|
||||
F: FnOnce(N) -> Self::Output,
|
||||
{
|
||||
Vis { inner: self, f, ph: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VisitorCtx<'a>: Sized {
|
||||
type Output;
|
||||
type Ctx;
|
||||
fn accept(self, node: &'a SyntaxNode) -> Result<Self::Output, Self::Ctx>;
|
||||
fn visit<N, F>(self, f: F) -> VisCtx<Self, N, F>
|
||||
where
|
||||
N: AstNode + 'a,
|
||||
F: FnOnce(N, Self::Ctx) -> Self::Output,
|
||||
{
|
||||
VisCtx { inner: self, f, ph: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EmptyVisitor<T> {
|
||||
ph: PhantomData<fn() -> T>,
|
||||
}
|
||||
|
||||
impl<'a, T> Visitor<'a> for EmptyVisitor<T> {
|
||||
type Output = T;
|
||||
|
||||
fn accept(self, _node: &'a SyntaxNode) -> Option<T> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EmptyVisitorCtx<T, C> {
|
||||
ctx: C,
|
||||
ph: PhantomData<fn() -> T>,
|
||||
}
|
||||
|
||||
impl<'a, T, C> VisitorCtx<'a> for EmptyVisitorCtx<T, C> {
|
||||
type Output = T;
|
||||
type Ctx = C;
|
||||
|
||||
fn accept(self, _node: &'a SyntaxNode) -> Result<T, C> {
|
||||
Err(self.ctx)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Vis<V, N, F> {
|
||||
inner: V,
|
||||
f: F,
|
||||
ph: PhantomData<fn(N)>,
|
||||
}
|
||||
|
||||
impl<'a, V, N, F> Visitor<'a> for Vis<V, N, F>
|
||||
where
|
||||
V: Visitor<'a>,
|
||||
N: AstNode + 'a,
|
||||
F: FnOnce(N) -> <V as Visitor<'a>>::Output,
|
||||
{
|
||||
type Output = <V as Visitor<'a>>::Output;
|
||||
|
||||
fn accept(self, node: &'a SyntaxNode) -> Option<Self::Output> {
|
||||
let Vis { inner, f, .. } = self;
|
||||
inner.accept(node).or_else(|| N::cast(node.clone()).map(f))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VisCtx<V, N, F> {
|
||||
inner: V,
|
||||
f: F,
|
||||
ph: PhantomData<fn(N)>,
|
||||
}
|
||||
|
||||
impl<'a, V, N, F> VisitorCtx<'a> for VisCtx<V, N, F>
|
||||
where
|
||||
V: VisitorCtx<'a>,
|
||||
N: AstNode + 'a,
|
||||
F: FnOnce(N, <V as VisitorCtx<'a>>::Ctx) -> <V as VisitorCtx<'a>>::Output,
|
||||
{
|
||||
type Output = <V as VisitorCtx<'a>>::Output;
|
||||
type Ctx = <V as VisitorCtx<'a>>::Ctx;
|
||||
|
||||
fn accept(self, node: &'a SyntaxNode) -> Result<Self::Output, Self::Ctx> {
|
||||
let VisCtx { inner, f, .. } = self;
|
||||
inner.accept(node).or_else(|ctx| match N::cast(node.clone()) {
|
||||
None => Err(ctx),
|
||||
Some(node) => Ok(f(node, ctx)),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -160,6 +160,17 @@ impl SourceFile {
|
|||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! match_ast {
|
||||
(match $node:ident {
|
||||
$( ast::$ast:ident($it:ident) => $res:block, )*
|
||||
_ => $catch_all:expr,
|
||||
}) => {{
|
||||
$( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
|
||||
{ $catch_all }
|
||||
}};
|
||||
}
|
||||
|
||||
/// This test does not assert anything and instead just shows off the crate's
|
||||
/// API.
|
||||
#[test]
|
||||
|
@ -294,7 +305,7 @@ fn api_walkthrough() {
|
|||
// To recursively process the tree, there are three approaches:
|
||||
// 1. explicitly call getter methods on AST nodes.
|
||||
// 2. use descendants and `AstNode::cast`.
|
||||
// 3. use descendants and the visitor.
|
||||
// 3. use descendants and `match_ast!`.
|
||||
//
|
||||
// Here's how the first one looks like:
|
||||
let exprs_cast: Vec<String> = file
|
||||
|
@ -304,17 +315,17 @@ fn api_walkthrough() {
|
|||
.map(|expr| expr.syntax().text().to_string())
|
||||
.collect();
|
||||
|
||||
// An alternative is to use a visitor. The visitor does not do traversal
|
||||
// automatically (so it's more akin to a generic lambda) and is constructed
|
||||
// from closures. This seems more flexible than a single generated visitor
|
||||
// trait.
|
||||
use algo::visit::{visitor, Visitor};
|
||||
// An alternative is to use a macro.
|
||||
let mut exprs_visit = Vec::new();
|
||||
for node in file.syntax().descendants() {
|
||||
if let Some(result) =
|
||||
visitor().visit::<ast::Expr, _>(|expr| expr.syntax().text().to_string()).accept(&node)
|
||||
{
|
||||
exprs_visit.push(result);
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::Expr(it) => {
|
||||
let res = it.syntax().text().to_string();
|
||||
exprs_visit.push(res);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(exprs_cast, exprs_visit);
|
||||
|
|
|
@ -5,8 +5,7 @@ mod block;
|
|||
use rustc_lexer::unescape;
|
||||
|
||||
use crate::{
|
||||
algo::visit::{visitor_ctx, VisitorCtx},
|
||||
ast, AstNode, SyntaxError, SyntaxErrorKind,
|
||||
ast, match_ast, AstNode, SyntaxError, SyntaxErrorKind,
|
||||
SyntaxKind::{BYTE, BYTE_STRING, CHAR, INT_NUMBER, STRING},
|
||||
SyntaxNode, SyntaxToken, TextUnit, T,
|
||||
};
|
||||
|
@ -97,12 +96,15 @@ impl From<rustc_lexer::unescape::EscapeError> for SyntaxErrorKind {
|
|||
pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
|
||||
let mut errors = Vec::new();
|
||||
for node in root.descendants() {
|
||||
let _ = visitor_ctx(&mut errors)
|
||||
.visit::<ast::Literal, _>(validate_literal)
|
||||
.visit::<ast::BlockExpr, _>(block::validate_block_expr)
|
||||
.visit::<ast::FieldExpr, _>(|it, errors| validate_numeric_name(it.name_ref(), errors))
|
||||
.visit::<ast::RecordField, _>(|it, errors| validate_numeric_name(it.name_ref(), errors))
|
||||
.accept(&node);
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::Literal(it) => { validate_literal(it, &mut errors) },
|
||||
ast::BlockExpr(it) => { block::validate_block_expr(it, &mut errors) },
|
||||
ast::FieldExpr(it) => { validate_numeric_name(it.name_ref(), &mut errors) },
|
||||
ast::RecordField(it) => { validate_numeric_name(it.name_ref(), &mut errors) },
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
errors
|
||||
}
|
||||
|
|
|
@ -79,9 +79,7 @@ Rust syntax tree structure and parser. See
|
|||
- `grammar.ron` RON description of the grammar, which is used to
|
||||
generate `syntax_kinds` and `ast` modules, using `cargo gen-syntax` command.
|
||||
- `algo`: generic tree algorithms, including `walk` for O(1) stack
|
||||
space tree traversal (this is cool) and `visit` for type-driven
|
||||
visiting the nodes (this is double plus cool, if you understand how
|
||||
`Visitor` works, you understand the design of syntax trees).
|
||||
space tree traversal (this is cool).
|
||||
|
||||
Tests for ra_syntax are mostly data-driven: `test_data/parser` contains subdirectories with a bunch of `.rs`
|
||||
(test vectors) and `.txt` files with corresponding syntax trees. During testing, we check
|
||||
|
|
|
@ -367,9 +367,9 @@ impl VariantData {
|
|||
|
||||
```rust
|
||||
// before:
|
||||
use algo:<|>:visitor::{Visitor, visit};
|
||||
use crate:<|>:db::{RootDatabase, FileSymbol};
|
||||
// after:
|
||||
use algo::{<|>visitor::{Visitor, visit}};
|
||||
use crate::{<|>db::{RootDatabase, FileSymbol}};
|
||||
```
|
||||
|
||||
- Flip binary expression
|
||||
|
|
Loading…
Reference in a new issue