diff options
-rw-r--r-- | crates/ra_assists/src/assists/split_import.rs | 6 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_fn_param.rs | 16 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_keyword.rs | 18 | ||||
-rw-r--r-- | crates/ra_ide_api/src/display/navigation_target.rs | 59 | ||||
-rw-r--r-- | crates/ra_ide_api/src/display/structure.rs | 116 | ||||
-rw-r--r-- | crates/ra_ide_api/src/goto_definition.rs | 185 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 77 | ||||
-rw-r--r-- | crates/ra_ide_api/src/inlay_hints.rs | 104 | ||||
-rw-r--r-- | crates/ra_ide_api/src/symbol_index.rs | 26 | ||||
-rw-r--r-- | crates/ra_syntax/src/algo.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/algo/visit.rs | 112 | ||||
-rw-r--r-- | crates/ra_syntax/src/lib.rs | 31 | ||||
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 18 | ||||
-rw-r--r-- | docs/dev/architecture.md | 4 | ||||
-rw-r--r-- | docs/user/features.md | 4 |
15 files changed, 349 insertions, 429 deletions
diff --git a/crates/ra_assists/src/assists/split_import.rs b/crates/ra_assists/src/assists/split_import.rs index 19d429daf..fe3e64af5 100644 --- a/crates/ra_assists/src/assists/split_import.rs +++ b/crates/ra_assists/src/assists/split_import.rs | |||
@@ -51,13 +51,13 @@ mod tests { | |||
51 | fn split_import_works_with_trees() { | 51 | fn split_import_works_with_trees() { |
52 | check_assist( | 52 | check_assist( |
53 | split_import, | 53 | split_import, |
54 | "use algo:<|>:visitor::{Visitor, visit}", | 54 | "use crate:<|>:db::{RootDatabase, FileSymbol}", |
55 | "use algo::{<|>visitor::{Visitor, visit}}", | 55 | "use crate::{<|>db::{RootDatabase, FileSymbol}}", |
56 | ) | 56 | ) |
57 | } | 57 | } |
58 | 58 | ||
59 | #[test] | 59 | #[test] |
60 | fn split_import_target() { | 60 | fn split_import_target() { |
61 | check_assist_target(split_import, "use algo::<|>visitor::{Visitor, visit}", "::"); | 61 | check_assist_target(split_import, "use crate::<|>db::{RootDatabase, FileSymbol}", "::"); |
62 | } | 62 | } |
63 | } | 63 | } |
diff --git a/crates/ra_ide_api/src/completion/complete_fn_param.rs b/crates/ra_ide_api/src/completion/complete_fn_param.rs index 844a63f6c..3e936e3ec 100644 --- a/crates/ra_ide_api/src/completion/complete_fn_param.rs +++ b/crates/ra_ide_api/src/completion/complete_fn_param.rs | |||
@@ -1,9 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ast, match_ast, AstNode}; |
4 | algo::visit::{visitor_ctx, VisitorCtx}, | ||
5 | ast, AstNode, | ||
6 | }; | ||
7 | use rustc_hash::FxHashMap; | 4 | use rustc_hash::FxHashMap; |
8 | 5 | ||
9 | use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; | 6 | use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions}; |
@@ -19,10 +16,13 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) | |||
19 | 16 | ||
20 | let mut params = FxHashMap::default(); | 17 | let mut params = FxHashMap::default(); |
21 | for node in ctx.token.parent().ancestors() { | 18 | for node in ctx.token.parent().ancestors() { |
22 | let _ = visitor_ctx(&mut params) | 19 | match_ast! { |
23 | .visit::<ast::SourceFile, _>(process) | 20 | match node { |
24 | .visit::<ast::ItemList, _>(process) | 21 | ast::SourceFile(it) => { process(it, &mut params) }, |
25 | .accept(&node); | 22 | ast::ItemList(it) => { process(it, &mut params) }, |
23 | _ => (), | ||
24 | } | ||
25 | } | ||
26 | } | 26 | } |
27 | params | 27 | params |
28 | .into_iter() | 28 | .into_iter() |
diff --git a/crates/ra_ide_api/src/completion/complete_keyword.rs b/crates/ra_ide_api/src/completion/complete_keyword.rs index 3f121d45c..48c688a08 100644 --- a/crates/ra_ide_api/src/completion/complete_keyword.rs +++ b/crates/ra_ide_api/src/completion/complete_keyword.rs | |||
@@ -1,9 +1,8 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | algo::visit::{visitor, Visitor}, | ||
5 | ast::{self, LoopBodyOwner}, | 4 | ast::{self, LoopBodyOwner}, |
6 | AstNode, | 5 | match_ast, AstNode, |
7 | SyntaxKind::*, | 6 | SyntaxKind::*, |
8 | SyntaxToken, | 7 | SyntaxToken, |
9 | }; | 8 | }; |
@@ -84,12 +83,15 @@ fn is_in_loop_body(leaf: &SyntaxToken) -> bool { | |||
84 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { | 83 | if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { |
85 | break; | 84 | break; |
86 | } | 85 | } |
87 | let loop_body = visitor() | 86 | let loop_body = match_ast! { |
88 | .visit::<ast::ForExpr, _>(|it| it.loop_body()) | 87 | match node { |
89 | .visit::<ast::WhileExpr, _>(|it| it.loop_body()) | 88 | ast::ForExpr(it) => { it.loop_body() }, |
90 | .visit::<ast::LoopExpr, _>(|it| it.loop_body()) | 89 | ast::WhileExpr(it) => { it.loop_body() }, |
91 | .accept(&node); | 90 | ast::LoopExpr(it) => { it.loop_body() }, |
92 | if let Some(Some(body)) = loop_body { | 91 | _ => None, |
92 | } | ||
93 | }; | ||
94 | if let Some(body) = loop_body { | ||
93 | if leaf.text_range().is_subrange(&body.syntax().text_range()) { | 95 | if leaf.text_range().is_subrange(&body.syntax().text_range()) { |
94 | return true; | 96 | return true; |
95 | } | 97 | } |
diff --git a/crates/ra_ide_api/src/display/navigation_target.rs b/crates/ra_ide_api/src/display/navigation_target.rs index 60ae802c0..d0b1a8a2a 100644 --- a/crates/ra_ide_api/src/display/navigation_target.rs +++ b/crates/ra_ide_api/src/display/navigation_target.rs | |||
@@ -3,9 +3,8 @@ | |||
3 | use hir::{AssocItem, FieldSource, HasSource, ModuleSource}; | 3 | use hir::{AssocItem, FieldSource, HasSource, ModuleSource}; |
4 | use ra_db::{FileId, SourceDatabase}; | 4 | use ra_db::{FileId, SourceDatabase}; |
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | algo::visit::{visitor, Visitor}, | ||
7 | ast::{self, DocCommentsOwner}, | 6 | ast::{self, DocCommentsOwner}, |
8 | AstNode, AstPtr, SmolStr, | 7 | match_ast, AstNode, AstPtr, SmolStr, |
9 | SyntaxKind::{self, NAME}, | 8 | SyntaxKind::{self, NAME}, |
10 | SyntaxNode, TextRange, | 9 | SyntaxNode, TextRange, |
11 | }; | 10 | }; |
@@ -308,19 +307,22 @@ pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option | |||
308 | let parse = db.parse(symbol.file_id); | 307 | let parse = db.parse(symbol.file_id); |
309 | let node = symbol.ptr.to_node(parse.tree().syntax()); | 308 | let node = symbol.ptr.to_node(parse.tree().syntax()); |
310 | 309 | ||
311 | visitor() | 310 | match_ast! { |
312 | .visit(|it: ast::FnDef| it.doc_comment_text()) | 311 | match node { |
313 | .visit(|it: ast::StructDef| it.doc_comment_text()) | 312 | ast::FnDef(it) => { it.doc_comment_text() }, |
314 | .visit(|it: ast::EnumDef| it.doc_comment_text()) | 313 | ast::StructDef(it) => { it.doc_comment_text() }, |
315 | .visit(|it: ast::TraitDef| it.doc_comment_text()) | 314 | ast::EnumDef(it) => { it.doc_comment_text() }, |
316 | .visit(|it: ast::Module| it.doc_comment_text()) | 315 | ast::TraitDef(it) => { it.doc_comment_text() }, |
317 | .visit(|it: ast::TypeAliasDef| it.doc_comment_text()) | 316 | ast::Module(it) => { it.doc_comment_text() }, |
318 | .visit(|it: ast::ConstDef| it.doc_comment_text()) | 317 | ast::TypeAliasDef(it) => { it.doc_comment_text() }, |
319 | .visit(|it: ast::StaticDef| it.doc_comment_text()) | 318 | ast::ConstDef(it) => { it.doc_comment_text() }, |
320 | .visit(|it: ast::RecordFieldDef| it.doc_comment_text()) | 319 | ast::StaticDef(it) => { it.doc_comment_text() }, |
321 | .visit(|it: ast::EnumVariant| it.doc_comment_text()) | 320 | ast::RecordFieldDef(it) => { it.doc_comment_text() }, |
322 | .visit(|it: ast::MacroCall| it.doc_comment_text()) | 321 | ast::EnumVariant(it) => { it.doc_comment_text() }, |
323 | .accept(&node)? | 322 | ast::MacroCall(it) => { it.doc_comment_text() }, |
323 | _ => None, | ||
324 | } | ||
325 | } | ||
324 | } | 326 | } |
325 | 327 | ||
326 | /// Get a description of a symbol. | 328 | /// Get a description of a symbol. |
@@ -330,16 +332,19 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> | |||
330 | let parse = db.parse(symbol.file_id); | 332 | let parse = db.parse(symbol.file_id); |
331 | let node = symbol.ptr.to_node(parse.tree().syntax()); | 333 | let node = symbol.ptr.to_node(parse.tree().syntax()); |
332 | 334 | ||
333 | visitor() | 335 | match_ast! { |
334 | .visit(|node: ast::FnDef| node.short_label()) | 336 | match node { |
335 | .visit(|node: ast::StructDef| node.short_label()) | 337 | ast::FnDef(it) => { it.short_label() }, |
336 | .visit(|node: ast::EnumDef| node.short_label()) | 338 | ast::StructDef(it) => { it.short_label() }, |
337 | .visit(|node: ast::TraitDef| node.short_label()) | 339 | ast::EnumDef(it) => { it.short_label() }, |
338 | .visit(|node: ast::Module| node.short_label()) | 340 | ast::TraitDef(it) => { it.short_label() }, |
339 | .visit(|node: ast::TypeAliasDef| node.short_label()) | 341 | ast::Module(it) => { it.short_label() }, |
340 | .visit(|node: ast::ConstDef| node.short_label()) | 342 | ast::TypeAliasDef(it) => { it.short_label() }, |
341 | .visit(|node: ast::StaticDef| node.short_label()) | 343 | ast::ConstDef(it) => { it.short_label() }, |
342 | .visit(|node: ast::RecordFieldDef| node.short_label()) | 344 | ast::StaticDef(it) => { it.short_label() }, |
343 | .visit(|node: ast::EnumVariant| node.short_label()) | 345 | ast::RecordFieldDef(it) => { it.short_label() }, |
344 | .accept(&node)? | 346 | ast::EnumVariant(it) => { it.short_label() }, |
347 | _ => None, | ||
348 | } | ||
349 | } | ||
345 | } | 350 | } |
diff --git a/crates/ra_ide_api/src/display/structure.rs b/crates/ra_ide_api/src/display/structure.rs index 8815df747..ddd8b7b20 100644 --- a/crates/ra_ide_api/src/display/structure.rs +++ b/crates/ra_ide_api/src/display/structure.rs | |||
@@ -3,9 +3,8 @@ | |||
3 | use crate::TextRange; | 3 | use crate::TextRange; |
4 | 4 | ||
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | algo::visit::{visitor, Visitor}, | ||
7 | ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, | 6 | ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, |
8 | AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent, | 7 | match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent, |
9 | }; | 8 | }; |
10 | 9 | ||
11 | #[derive(Debug, Clone)] | 10 | #[derive(Debug, Clone)] |
@@ -101,63 +100,66 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> { | |||
101 | }) | 100 | }) |
102 | } | 101 | } |
103 | 102 | ||
104 | visitor() | 103 | match_ast! { |
105 | .visit(|fn_def: ast::FnDef| { | 104 | match node { |
106 | let mut detail = String::from("fn"); | 105 | ast::FnDef(it) => { |
107 | if let Some(type_param_list) = fn_def.type_param_list() { | 106 | let mut detail = String::from("fn"); |
108 | collapse_ws(type_param_list.syntax(), &mut detail); | 107 | if let Some(type_param_list) = it.type_param_list() { |
109 | } | 108 | collapse_ws(type_param_list.syntax(), &mut detail); |
110 | if let Some(param_list) = fn_def.param_list() { | 109 | } |
111 | collapse_ws(param_list.syntax(), &mut detail); | 110 | if let Some(param_list) = it.param_list() { |
112 | } | 111 | collapse_ws(param_list.syntax(), &mut detail); |
113 | if let Some(ret_type) = fn_def.ret_type() { | 112 | } |
114 | detail.push_str(" "); | 113 | if let Some(ret_type) = it.ret_type() { |
115 | collapse_ws(ret_type.syntax(), &mut detail); | 114 | detail.push_str(" "); |
116 | } | 115 | collapse_ws(ret_type.syntax(), &mut detail); |
117 | |||
118 | decl_with_detail(fn_def, Some(detail)) | ||
119 | }) | ||
120 | .visit(decl::<ast::StructDef>) | ||
121 | .visit(decl::<ast::EnumDef>) | ||
122 | .visit(decl::<ast::EnumVariant>) | ||
123 | .visit(decl::<ast::TraitDef>) | ||
124 | .visit(decl::<ast::Module>) | ||
125 | .visit(|td: ast::TypeAliasDef| { | ||
126 | let ty = td.type_ref(); | ||
127 | decl_with_type_ref(td, ty) | ||
128 | }) | ||
129 | .visit(decl_with_ascription::<ast::RecordFieldDef>) | ||
130 | .visit(decl_with_ascription::<ast::ConstDef>) | ||
131 | .visit(decl_with_ascription::<ast::StaticDef>) | ||
132 | .visit(|im: ast::ImplBlock| { | ||
133 | let target_type = im.target_type()?; | ||
134 | let target_trait = im.target_trait(); | ||
135 | let label = match target_trait { | ||
136 | None => format!("impl {}", target_type.syntax().text()), | ||
137 | Some(t) => { | ||
138 | format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),) | ||
139 | } | 116 | } |
140 | }; | ||
141 | 117 | ||
142 | let node = StructureNode { | 118 | decl_with_detail(it, Some(detail)) |
143 | parent: None, | 119 | }, |
144 | label, | 120 | ast::StructDef(it) => { decl(it) }, |
145 | navigation_range: target_type.syntax().text_range(), | 121 | ast::EnumDef(it) => { decl(it) }, |
146 | node_range: im.syntax().text_range(), | 122 | ast::EnumVariant(it) => { decl(it) }, |
147 | kind: im.syntax().kind(), | 123 | ast::TraitDef(it) => { decl(it) }, |
148 | detail: None, | 124 | ast::Module(it) => { decl(it) }, |
149 | deprecated: false, | 125 | ast::TypeAliasDef(it) => { |
150 | }; | 126 | let ty = it.type_ref(); |
151 | Some(node) | 127 | decl_with_type_ref(it, ty) |
152 | }) | 128 | }, |
153 | .visit(|mc: ast::MacroCall| { | 129 | ast::RecordFieldDef(it) => { decl_with_ascription(it) }, |
154 | let first_token = mc.syntax().first_token().unwrap(); | 130 | ast::ConstDef(it) => { decl_with_ascription(it) }, |
155 | if first_token.text().as_str() != "macro_rules" { | 131 | ast::StaticDef(it) => { decl_with_ascription(it) }, |
156 | return None; | 132 | ast::ImplBlock(it) => { |
157 | } | 133 | let target_type = it.target_type()?; |
158 | decl(mc) | 134 | let target_trait = it.target_trait(); |
159 | }) | 135 | let label = match target_trait { |
160 | .accept(&node)? | 136 | None => format!("impl {}", target_type.syntax().text()), |
137 | Some(t) => { | ||
138 | format!("impl {} for {}", t.syntax().text(), target_type.syntax().text(),) | ||
139 | } | ||
140 | }; | ||
141 | |||
142 | let node = StructureNode { | ||
143 | parent: None, | ||
144 | label, | ||
145 | navigation_range: target_type.syntax().text_range(), | ||
146 | node_range: it.syntax().text_range(), | ||
147 | kind: it.syntax().kind(), | ||
148 | detail: None, | ||
149 | deprecated: false, | ||
150 | }; | ||
151 | Some(node) | ||
152 | }, | ||
153 | ast::MacroCall(it) => { | ||
154 | let first_token = it.syntax().first_token().unwrap(); | ||
155 | if first_token.text().as_str() != "macro_rules" { | ||
156 | return None; | ||
157 | } | ||
158 | decl(it) | ||
159 | }, | ||
160 | _ => None, | ||
161 | } | ||
162 | } | ||
161 | } | 163 | } |
162 | 164 | ||
163 | #[cfg(test)] | 165 | #[cfg(test)] |
diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 567d4a674..41a88314f 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs | |||
@@ -2,12 +2,9 @@ | |||
2 | 2 | ||
3 | use ra_db::{FileId, SourceDatabase}; | 3 | use ra_db::{FileId, SourceDatabase}; |
4 | use ra_syntax::{ | 4 | use ra_syntax::{ |
5 | algo::{ | 5 | algo::find_node_at_offset, |
6 | find_node_at_offset, | ||
7 | visit::{visitor, Visitor}, | ||
8 | }, | ||
9 | ast::{self, DocCommentsOwner}, | 6 | ast::{self, DocCommentsOwner}, |
10 | AstNode, SyntaxNode, | 7 | match_ast, AstNode, SyntaxNode, |
11 | }; | 8 | }; |
12 | 9 | ||
13 | use crate::{ | 10 | use crate::{ |
@@ -114,91 +111,99 @@ pub(crate) fn name_definition( | |||
114 | } | 111 | } |
115 | 112 | ||
116 | fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> { | 113 | fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> { |
117 | visitor() | 114 | match_ast! { |
118 | .visit(|node: ast::StructDef| { | 115 | match node { |
119 | NavigationTarget::from_named( | 116 | ast::StructDef(it) => { |
120 | file_id, | 117 | Some(NavigationTarget::from_named( |
121 | &node, | 118 | file_id, |
122 | node.doc_comment_text(), | 119 | &it, |
123 | node.short_label(), | 120 | it.doc_comment_text(), |
124 | ) | 121 | it.short_label(), |
125 | }) | 122 | )) |
126 | .visit(|node: ast::EnumDef| { | 123 | }, |
127 | NavigationTarget::from_named( | 124 | ast::EnumDef(it) => { |
128 | file_id, | 125 | Some(NavigationTarget::from_named( |
129 | &node, | 126 | file_id, |
130 | node.doc_comment_text(), | 127 | &it, |
131 | node.short_label(), | 128 | it.doc_comment_text(), |
132 | ) | 129 | it.short_label(), |
133 | }) | 130 | )) |
134 | .visit(|node: ast::EnumVariant| { | 131 | }, |
135 | NavigationTarget::from_named( | 132 | ast::EnumVariant(it) => { |
136 | file_id, | 133 | Some(NavigationTarget::from_named( |
137 | &node, | 134 | file_id, |
138 | node.doc_comment_text(), | 135 | &it, |
139 | node.short_label(), | 136 | it.doc_comment_text(), |
140 | ) | 137 | it.short_label(), |
141 | }) | 138 | )) |
142 | .visit(|node: ast::FnDef| { | 139 | }, |
143 | NavigationTarget::from_named( | 140 | ast::FnDef(it) => { |
144 | file_id, | 141 | Some(NavigationTarget::from_named( |
145 | &node, | 142 | file_id, |
146 | node.doc_comment_text(), | 143 | &it, |
147 | node.short_label(), | 144 | it.doc_comment_text(), |
148 | ) | 145 | it.short_label(), |
149 | }) | 146 | )) |
150 | .visit(|node: ast::TypeAliasDef| { | 147 | }, |
151 | NavigationTarget::from_named( | 148 | ast::TypeAliasDef(it) => { |
152 | file_id, | 149 | Some(NavigationTarget::from_named( |
153 | &node, | 150 | file_id, |
154 | node.doc_comment_text(), | 151 | &it, |
155 | node.short_label(), | 152 | it.doc_comment_text(), |
156 | ) | 153 | it.short_label(), |
157 | }) | 154 | )) |
158 | .visit(|node: ast::ConstDef| { | 155 | }, |
159 | NavigationTarget::from_named( | 156 | ast::ConstDef(it) => { |
160 | file_id, | 157 | Some(NavigationTarget::from_named( |
161 | &node, | 158 | file_id, |
162 | node.doc_comment_text(), | 159 | &it, |
163 | node.short_label(), | 160 | it.doc_comment_text(), |
164 | ) | 161 | it.short_label(), |
165 | }) | 162 | )) |
166 | .visit(|node: ast::StaticDef| { | 163 | }, |
167 | NavigationTarget::from_named( | 164 | ast::StaticDef(it) => { |
168 | file_id, | 165 | Some(NavigationTarget::from_named( |
169 | &node, | 166 | file_id, |
170 | node.doc_comment_text(), | 167 | &it, |
171 | node.short_label(), | 168 | it.doc_comment_text(), |
172 | ) | 169 | it.short_label(), |
173 | }) | 170 | )) |
174 | .visit(|node: ast::TraitDef| { | 171 | }, |
175 | NavigationTarget::from_named( | 172 | ast::TraitDef(it) => { |
176 | file_id, | 173 | Some(NavigationTarget::from_named( |
177 | &node, | 174 | file_id, |
178 | node.doc_comment_text(), | 175 | &it, |
179 | node.short_label(), | 176 | it.doc_comment_text(), |
180 | ) | 177 | it.short_label(), |
181 | }) | 178 | )) |
182 | .visit(|node: ast::RecordFieldDef| { | 179 | }, |
183 | NavigationTarget::from_named( | 180 | ast::RecordFieldDef(it) => { |
184 | file_id, | 181 | Some(NavigationTarget::from_named( |
185 | &node, | 182 | file_id, |
186 | node.doc_comment_text(), | 183 | &it, |
187 | node.short_label(), | 184 | it.doc_comment_text(), |
188 | ) | 185 | it.short_label(), |
189 | }) | 186 | )) |
190 | .visit(|node: ast::Module| { | 187 | }, |
191 | NavigationTarget::from_named( | 188 | ast::Module(it) => { |
192 | file_id, | 189 | Some(NavigationTarget::from_named( |
193 | &node, | 190 | file_id, |
194 | node.doc_comment_text(), | 191 | &it, |
195 | node.short_label(), | 192 | it.doc_comment_text(), |
196 | ) | 193 | it.short_label(), |
197 | }) | 194 | )) |
198 | .visit(|node: ast::MacroCall| { | 195 | }, |
199 | NavigationTarget::from_named(file_id, &node, node.doc_comment_text(), None) | 196 | ast::MacroCall(it) => { |
200 | }) | 197 | Some(NavigationTarget::from_named( |
201 | .accept(node) | 198 | file_id, |
199 | &it, | ||
200 | it.doc_comment_text(), | ||
201 | None, | ||
202 | )) | ||
203 | }, | ||
204 | _ => None, | ||
205 | } | ||
206 | } | ||
202 | } | 207 | } |
203 | 208 | ||
204 | #[cfg(test)] | 209 | #[cfg(test)] |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 200b57679..24b161c5c 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -3,12 +3,9 @@ | |||
3 | use hir::{Adt, HasSource, HirDisplay}; | 3 | use hir::{Adt, HasSource, HirDisplay}; |
4 | use ra_db::SourceDatabase; | 4 | use ra_db::SourceDatabase; |
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | algo::{ | 6 | algo::{ancestors_at_offset, find_covering_element, find_node_at_offset}, |
7 | ancestors_at_offset, find_covering_element, find_node_at_offset, | ||
8 | visit::{visitor, Visitor}, | ||
9 | }, | ||
10 | ast::{self, DocCommentsOwner}, | 7 | ast::{self, DocCommentsOwner}, |
11 | AstNode, | 8 | match_ast, AstNode, |
12 | }; | 9 | }; |
13 | 10 | ||
14 | use crate::{ | 11 | use crate::{ |
@@ -178,37 +175,45 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
178 | } | 175 | } |
179 | } else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) { | 176 | } else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) { |
180 | if let Some(parent) = name.syntax().parent() { | 177 | if let Some(parent) = name.syntax().parent() { |
181 | let text = visitor() | 178 | let text = match_ast! { |
182 | .visit(|node: ast::StructDef| { | 179 | match parent { |
183 | hover_text(node.doc_comment_text(), node.short_label()) | 180 | ast::StructDef(it) => { |
184 | }) | 181 | hover_text(it.doc_comment_text(), it.short_label()) |
185 | .visit(|node: ast::EnumDef| hover_text(node.doc_comment_text(), node.short_label())) | 182 | }, |
186 | .visit(|node: ast::EnumVariant| { | 183 | ast::EnumDef(it) => { |
187 | hover_text(node.doc_comment_text(), node.short_label()) | 184 | hover_text(it.doc_comment_text(), it.short_label()) |
188 | }) | 185 | }, |
189 | .visit(|node: ast::FnDef| hover_text(node.doc_comment_text(), node.short_label())) | 186 | ast::EnumVariant(it) => { |
190 | .visit(|node: ast::TypeAliasDef| { | 187 | hover_text(it.doc_comment_text(), it.short_label()) |
191 | hover_text(node.doc_comment_text(), node.short_label()) | 188 | }, |
192 | }) | 189 | ast::FnDef(it) => { |
193 | .visit(|node: ast::ConstDef| { | 190 | hover_text(it.doc_comment_text(), it.short_label()) |
194 | hover_text(node.doc_comment_text(), node.short_label()) | 191 | }, |
195 | }) | 192 | ast::TypeAliasDef(it) => { |
196 | .visit(|node: ast::StaticDef| { | 193 | hover_text(it.doc_comment_text(), it.short_label()) |
197 | hover_text(node.doc_comment_text(), node.short_label()) | 194 | }, |
198 | }) | 195 | ast::ConstDef(it) => { |
199 | .visit(|node: ast::TraitDef| { | 196 | hover_text(it.doc_comment_text(), it.short_label()) |
200 | hover_text(node.doc_comment_text(), node.short_label()) | 197 | }, |
201 | }) | 198 | ast::StaticDef(it) => { |
202 | .visit(|node: ast::RecordFieldDef| { | 199 | hover_text(it.doc_comment_text(), it.short_label()) |
203 | hover_text(node.doc_comment_text(), node.short_label()) | 200 | }, |
204 | }) | 201 | ast::TraitDef(it) => { |
205 | .visit(|node: ast::Module| hover_text(node.doc_comment_text(), node.short_label())) | 202 | hover_text(it.doc_comment_text(), it.short_label()) |
206 | .visit(|node: ast::MacroCall| hover_text(node.doc_comment_text(), None)) | 203 | }, |
207 | .accept(&parent); | 204 | ast::RecordFieldDef(it) => { |
208 | 205 | hover_text(it.doc_comment_text(), it.short_label()) | |
209 | if let Some(text) = text { | 206 | }, |
210 | res.extend(text); | 207 | ast::Module(it) => { |
211 | } | 208 | hover_text(it.doc_comment_text(), it.short_label()) |
209 | }, | ||
210 | ast::MacroCall(it) => { | ||
211 | hover_text(it.doc_comment_text(), None) | ||
212 | }, | ||
213 | _ => None, | ||
214 | } | ||
215 | }; | ||
216 | res.extend(text); | ||
212 | } | 217 | } |
213 | 218 | ||
214 | if !res.is_empty() && range.is_none() { | 219 | if !res.is_empty() && range.is_none() { |
diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs index 9b45575f8..f1c0dc164 100644 --- a/crates/ra_ide_api/src/inlay_hints.rs +++ b/crates/ra_ide_api/src/inlay_hints.rs | |||
@@ -3,9 +3,8 @@ | |||
3 | use crate::{db::RootDatabase, FileId}; | 3 | use crate::{db::RootDatabase, FileId}; |
4 | use hir::{HirDisplay, SourceAnalyzer, Ty}; | 4 | use hir::{HirDisplay, SourceAnalyzer, Ty}; |
5 | use ra_syntax::{ | 5 | use ra_syntax::{ |
6 | algo::visit::{visitor, Visitor}, | ||
7 | ast::{self, AstNode, TypeAscriptionOwner}, | 6 | ast::{self, AstNode, TypeAscriptionOwner}, |
8 | SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, | 7 | match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, |
9 | }; | 8 | }; |
10 | 9 | ||
11 | #[derive(Debug, PartialEq, Eq)] | 10 | #[derive(Debug, PartialEq, Eq)] |
@@ -33,55 +32,58 @@ fn get_inlay_hints( | |||
33 | file_id: FileId, | 32 | file_id: FileId, |
34 | node: &SyntaxNode, | 33 | node: &SyntaxNode, |
35 | ) -> Option<Vec<InlayHint>> { | 34 | ) -> Option<Vec<InlayHint>> { |
36 | visitor() | 35 | match_ast! { |
37 | .visit(|let_statement: ast::LetStmt| { | 36 | match node { |
38 | if let_statement.ascribed_type().is_some() { | 37 | ast::LetStmt(it) => { |
39 | return None; | 38 | if it.ascribed_type().is_some() { |
40 | } | 39 | return None; |
41 | let pat = let_statement.pat()?; | 40 | } |
42 | let analyzer = SourceAnalyzer::new(db, file_id, let_statement.syntax(), None); | 41 | let pat = it.pat()?; |
43 | Some(get_pat_type_hints(db, &analyzer, pat, false)) | 42 | let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None); |
44 | }) | 43 | Some(get_pat_type_hints(db, &analyzer, pat, false)) |
45 | .visit(|closure_parameter: ast::LambdaExpr| { | 44 | }, |
46 | let analyzer = SourceAnalyzer::new(db, file_id, closure_parameter.syntax(), None); | 45 | ast::LambdaExpr(it) => { |
47 | closure_parameter.param_list().map(|param_list| { | 46 | let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None); |
48 | param_list | 47 | it.param_list().map(|param_list| { |
49 | .params() | 48 | param_list |
50 | .filter(|closure_param| closure_param.ascribed_type().is_none()) | 49 | .params() |
51 | .filter_map(|closure_param| closure_param.pat()) | 50 | .filter(|closure_param| closure_param.ascribed_type().is_none()) |
52 | .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, false)) | 51 | .filter_map(|closure_param| closure_param.pat()) |
53 | .flatten() | 52 | .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, false)) |
54 | .collect() | 53 | .flatten() |
55 | }) | 54 | .collect() |
56 | }) | 55 | }) |
57 | .visit(|for_expression: ast::ForExpr| { | 56 | }, |
58 | let pat = for_expression.pat()?; | 57 | ast::ForExpr(it) => { |
59 | let analyzer = SourceAnalyzer::new(db, file_id, for_expression.syntax(), None); | 58 | let pat = it.pat()?; |
60 | Some(get_pat_type_hints(db, &analyzer, pat, false)) | 59 | let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None); |
61 | }) | 60 | Some(get_pat_type_hints(db, &analyzer, pat, false)) |
62 | .visit(|if_expr: ast::IfExpr| { | 61 | }, |
63 | let pat = if_expr.condition()?.pat()?; | 62 | ast::IfExpr(it) => { |
64 | let analyzer = SourceAnalyzer::new(db, file_id, if_expr.syntax(), None); | 63 | let pat = it.condition()?.pat()?; |
65 | Some(get_pat_type_hints(db, &analyzer, pat, true)) | 64 | let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None); |
66 | }) | 65 | Some(get_pat_type_hints(db, &analyzer, pat, true)) |
67 | .visit(|while_expr: ast::WhileExpr| { | 66 | }, |
68 | let pat = while_expr.condition()?.pat()?; | 67 | ast::WhileExpr(it) => { |
69 | let analyzer = SourceAnalyzer::new(db, file_id, while_expr.syntax(), None); | 68 | let pat = it.condition()?.pat()?; |
70 | Some(get_pat_type_hints(db, &analyzer, pat, true)) | 69 | let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None); |
71 | }) | 70 | Some(get_pat_type_hints(db, &analyzer, pat, true)) |
72 | .visit(|match_arm_list: ast::MatchArmList| { | 71 | }, |
73 | let analyzer = SourceAnalyzer::new(db, file_id, match_arm_list.syntax(), None); | 72 | ast::MatchArmList(it) => { |
74 | Some( | 73 | let analyzer = SourceAnalyzer::new(db, file_id, it.syntax(), None); |
75 | match_arm_list | 74 | Some( |
76 | .arms() | 75 | it |
77 | .map(|match_arm| match_arm.pats()) | 76 | .arms() |
78 | .flatten() | 77 | .map(|match_arm| match_arm.pats()) |
79 | .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, true)) | 78 | .flatten() |
80 | .flatten() | 79 | .map(|root_pat| get_pat_type_hints(db, &analyzer, root_pat, true)) |
81 | .collect(), | 80 | .flatten() |
82 | ) | 81 | .collect(), |
83 | }) | 82 | ) |
84 | .accept(&node)? | 83 | }, |
84 | _ => None, | ||
85 | } | ||
86 | } | ||
85 | } | 87 | } |
86 | 88 | ||
87 | fn get_pat_type_hints( | 89 | fn get_pat_type_hints( |
diff --git a/crates/ra_ide_api/src/symbol_index.rs b/crates/ra_ide_api/src/symbol_index.rs index 02cdfbc60..797e9926f 100644 --- a/crates/ra_ide_api/src/symbol_index.rs +++ b/crates/ra_ide_api/src/symbol_index.rs | |||
@@ -32,9 +32,8 @@ use ra_db::{ | |||
32 | SourceDatabase, SourceRootId, | 32 | SourceDatabase, SourceRootId, |
33 | }; | 33 | }; |
34 | use ra_syntax::{ | 34 | use ra_syntax::{ |
35 | algo::visit::{visitor, Visitor}, | ||
36 | ast::{self, NameOwner}, | 35 | ast::{self, NameOwner}, |
37 | AstNode, Parse, SmolStr, SourceFile, | 36 | match_ast, AstNode, Parse, SmolStr, SourceFile, |
38 | SyntaxKind::{self, *}, | 37 | SyntaxKind::{self, *}, |
39 | SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent, | 38 | SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent, |
40 | }; | 39 | }; |
@@ -306,16 +305,19 @@ fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { | |||
306 | 305 | ||
307 | Some((name, ptr, name_range)) | 306 | Some((name, ptr, name_range)) |
308 | } | 307 | } |
309 | visitor() | 308 | match_ast! { |
310 | .visit(decl::<ast::FnDef>) | 309 | match node { |
311 | .visit(decl::<ast::StructDef>) | 310 | ast::FnDef(it) => { decl(it) }, |
312 | .visit(decl::<ast::EnumDef>) | 311 | ast::StructDef(it) => { decl(it) }, |
313 | .visit(decl::<ast::TraitDef>) | 312 | ast::EnumDef(it) => { decl(it) }, |
314 | .visit(decl::<ast::Module>) | 313 | ast::TraitDef(it) => { decl(it) }, |
315 | .visit(decl::<ast::TypeAliasDef>) | 314 | ast::Module(it) => { decl(it) }, |
316 | .visit(decl::<ast::ConstDef>) | 315 | ast::TypeAliasDef(it) => { decl(it) }, |
317 | .visit(decl::<ast::StaticDef>) | 316 | ast::ConstDef(it) => { decl(it) }, |
318 | .accept(node)? | 317 | ast::StaticDef(it) => { decl(it) }, |
318 | _ => None, | ||
319 | } | ||
320 | } | ||
319 | } | 321 | } |
320 | 322 | ||
321 | fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> { | 323 | fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option<FileSymbol> { |
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index d55534ede..7cfea70f9 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -1,7 +1,5 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | pub mod visit; | ||
4 | |||
5 | use std::ops::RangeInclusive; | 3 | use std::ops::RangeInclusive; |
6 | 4 | ||
7 | use itertools::Itertools; | 5 | use itertools::Itertools; |
diff --git a/crates/ra_syntax/src/algo/visit.rs b/crates/ra_syntax/src/algo/visit.rs deleted file mode 100644 index 4df275ba4..000000000 --- a/crates/ra_syntax/src/algo/visit.rs +++ /dev/null | |||
@@ -1,112 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use crate::{AstNode, SyntaxNode}; | ||
4 | |||
5 | use std::marker::PhantomData; | ||
6 | |||
7 | pub fn visitor<'a, T>() -> impl Visitor<'a, Output = T> { | ||
8 | EmptyVisitor { ph: PhantomData } | ||
9 | } | ||
10 | |||
11 | pub fn visitor_ctx<'a, T, C>(ctx: C) -> impl VisitorCtx<'a, Output = T, Ctx = C> { | ||
12 | EmptyVisitorCtx { ph: PhantomData, ctx } | ||
13 | } | ||
14 | |||
15 | pub trait Visitor<'a>: Sized { | ||
16 | type Output; | ||
17 | fn accept(self, node: &'a SyntaxNode) -> Option<Self::Output>; | ||
18 | fn visit<N, F>(self, f: F) -> Vis<Self, N, F> | ||
19 | where | ||
20 | N: AstNode + 'a, | ||
21 | F: FnOnce(N) -> Self::Output, | ||
22 | { | ||
23 | Vis { inner: self, f, ph: PhantomData } | ||
24 | } | ||
25 | } | ||
26 | |||
27 | pub trait VisitorCtx<'a>: Sized { | ||
28 | type Output; | ||
29 | type Ctx; | ||
30 | fn accept(self, node: &'a SyntaxNode) -> Result<Self::Output, Self::Ctx>; | ||
31 | fn visit<N, F>(self, f: F) -> VisCtx<Self, N, F> | ||
32 | where | ||
33 | N: AstNode + 'a, | ||
34 | F: FnOnce(N, Self::Ctx) -> Self::Output, | ||
35 | { | ||
36 | VisCtx { inner: self, f, ph: PhantomData } | ||
37 | } | ||
38 | } | ||
39 | |||
40 | #[derive(Debug)] | ||
41 | struct EmptyVisitor<T> { | ||
42 | ph: PhantomData<fn() -> T>, | ||
43 | } | ||
44 | |||
45 | impl<'a, T> Visitor<'a> for EmptyVisitor<T> { | ||
46 | type Output = T; | ||
47 | |||
48 | fn accept(self, _node: &'a SyntaxNode) -> Option<T> { | ||
49 | None | ||
50 | } | ||
51 | } | ||
52 | |||
53 | #[derive(Debug)] | ||
54 | struct EmptyVisitorCtx<T, C> { | ||
55 | ctx: C, | ||
56 | ph: PhantomData<fn() -> T>, | ||
57 | } | ||
58 | |||
59 | impl<'a, T, C> VisitorCtx<'a> for EmptyVisitorCtx<T, C> { | ||
60 | type Output = T; | ||
61 | type Ctx = C; | ||
62 | |||
63 | fn accept(self, _node: &'a SyntaxNode) -> Result<T, C> { | ||
64 | Err(self.ctx) | ||
65 | } | ||
66 | } | ||
67 | |||
68 | #[derive(Debug)] | ||
69 | pub struct Vis<V, N, F> { | ||
70 | inner: V, | ||
71 | f: F, | ||
72 | ph: PhantomData<fn(N)>, | ||
73 | } | ||
74 | |||
75 | impl<'a, V, N, F> Visitor<'a> for Vis<V, N, F> | ||
76 | where | ||
77 | V: Visitor<'a>, | ||
78 | N: AstNode + 'a, | ||
79 | F: FnOnce(N) -> <V as Visitor<'a>>::Output, | ||
80 | { | ||
81 | type Output = <V as Visitor<'a>>::Output; | ||
82 | |||
83 | fn accept(self, node: &'a SyntaxNode) -> Option<Self::Output> { | ||
84 | let Vis { inner, f, .. } = self; | ||
85 | inner.accept(node).or_else(|| N::cast(node.clone()).map(f)) | ||
86 | } | ||
87 | } | ||
88 | |||
89 | #[derive(Debug)] | ||
90 | pub struct VisCtx<V, N, F> { | ||
91 | inner: V, | ||
92 | f: F, | ||
93 | ph: PhantomData<fn(N)>, | ||
94 | } | ||
95 | |||
96 | impl<'a, V, N, F> VisitorCtx<'a> for VisCtx<V, N, F> | ||
97 | where | ||
98 | V: VisitorCtx<'a>, | ||
99 | N: AstNode + 'a, | ||
100 | F: FnOnce(N, <V as VisitorCtx<'a>>::Ctx) -> <V as VisitorCtx<'a>>::Output, | ||
101 | { | ||
102 | type Output = <V as VisitorCtx<'a>>::Output; | ||
103 | type Ctx = <V as VisitorCtx<'a>>::Ctx; | ||
104 | |||
105 | fn accept(self, node: &'a SyntaxNode) -> Result<Self::Output, Self::Ctx> { | ||
106 | let VisCtx { inner, f, .. } = self; | ||
107 | inner.accept(node).or_else(|ctx| match N::cast(node.clone()) { | ||
108 | None => Err(ctx), | ||
109 | Some(node) => Ok(f(node, ctx)), | ||
110 | }) | ||
111 | } | ||
112 | } | ||
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index edb6076bb..c315ba552 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs | |||
@@ -160,6 +160,17 @@ impl SourceFile { | |||
160 | } | 160 | } |
161 | } | 161 | } |
162 | 162 | ||
163 | #[macro_export] | ||
164 | macro_rules! match_ast { | ||
165 | (match $node:ident { | ||
166 | $( ast::$ast:ident($it:ident) => $res:block, )* | ||
167 | _ => $catch_all:expr, | ||
168 | }) => {{ | ||
169 | $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* | ||
170 | { $catch_all } | ||
171 | }}; | ||
172 | } | ||
173 | |||
163 | /// This test does not assert anything and instead just shows off the crate's | 174 | /// This test does not assert anything and instead just shows off the crate's |
164 | /// API. | 175 | /// API. |
165 | #[test] | 176 | #[test] |
@@ -294,7 +305,7 @@ fn api_walkthrough() { | |||
294 | // To recursively process the tree, there are three approaches: | 305 | // To recursively process the tree, there are three approaches: |
295 | // 1. explicitly call getter methods on AST nodes. | 306 | // 1. explicitly call getter methods on AST nodes. |
296 | // 2. use descendants and `AstNode::cast`. | 307 | // 2. use descendants and `AstNode::cast`. |
297 | // 3. use descendants and the visitor. | 308 | // 3. use descendants and `match_ast!`. |
298 | // | 309 | // |
299 | // Here's how the first one looks like: | 310 | // Here's how the first one looks like: |
300 | let exprs_cast: Vec<String> = file | 311 | let exprs_cast: Vec<String> = file |
@@ -304,17 +315,17 @@ fn api_walkthrough() { | |||
304 | .map(|expr| expr.syntax().text().to_string()) | 315 | .map(|expr| expr.syntax().text().to_string()) |
305 | .collect(); | 316 | .collect(); |
306 | 317 | ||
307 | // An alternative is to use a visitor. The visitor does not do traversal | 318 | // An alternative is to use a macro. |
308 | // automatically (so it's more akin to a generic lambda) and is constructed | ||
309 | // from closures. This seems more flexible than a single generated visitor | ||
310 | // trait. | ||
311 | use algo::visit::{visitor, Visitor}; | ||
312 | let mut exprs_visit = Vec::new(); | 319 | let mut exprs_visit = Vec::new(); |
313 | for node in file.syntax().descendants() { | 320 | for node in file.syntax().descendants() { |
314 | if let Some(result) = | 321 | match_ast! { |
315 | visitor().visit::<ast::Expr, _>(|expr| expr.syntax().text().to_string()).accept(&node) | 322 | match node { |
316 | { | 323 | ast::Expr(it) => { |
317 | exprs_visit.push(result); | 324 | let res = it.syntax().text().to_string(); |
325 | exprs_visit.push(res); | ||
326 | }, | ||
327 | _ => (), | ||
328 | } | ||
318 | } | 329 | } |
319 | } | 330 | } |
320 | assert_eq!(exprs_cast, exprs_visit); | 331 | assert_eq!(exprs_cast, exprs_visit); |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 4f8935b2c..ab4f15908 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -5,8 +5,7 @@ mod block; | |||
5 | use rustc_lexer::unescape; | 5 | use rustc_lexer::unescape; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
8 | algo::visit::{visitor_ctx, VisitorCtx}, | 8 | ast, match_ast, AstNode, SyntaxError, SyntaxErrorKind, |
9 | ast, AstNode, SyntaxError, SyntaxErrorKind, | ||
10 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, INT_NUMBER, STRING}, | 9 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, INT_NUMBER, STRING}, |
11 | SyntaxNode, SyntaxToken, TextUnit, T, | 10 | SyntaxNode, SyntaxToken, TextUnit, T, |
12 | }; | 11 | }; |
@@ -97,12 +96,15 @@ impl From<rustc_lexer::unescape::EscapeError> for SyntaxErrorKind { | |||
97 | pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | 96 | pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { |
98 | let mut errors = Vec::new(); | 97 | let mut errors = Vec::new(); |
99 | for node in root.descendants() { | 98 | for node in root.descendants() { |
100 | let _ = visitor_ctx(&mut errors) | 99 | match_ast! { |
101 | .visit::<ast::Literal, _>(validate_literal) | 100 | match node { |
102 | .visit::<ast::BlockExpr, _>(block::validate_block_expr) | 101 | ast::Literal(it) => { validate_literal(it, &mut errors) }, |
103 | .visit::<ast::FieldExpr, _>(|it, errors| validate_numeric_name(it.name_ref(), errors)) | 102 | ast::BlockExpr(it) => { block::validate_block_expr(it, &mut errors) }, |
104 | .visit::<ast::RecordField, _>(|it, errors| validate_numeric_name(it.name_ref(), errors)) | 103 | ast::FieldExpr(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, |
105 | .accept(&node); | 104 | ast::RecordField(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, |
105 | _ => (), | ||
106 | } | ||
107 | } | ||
106 | } | 108 | } |
107 | errors | 109 | errors |
108 | } | 110 | } |
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index 1201f6e5a..1ffabc6ef 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md | |||
@@ -79,9 +79,7 @@ Rust syntax tree structure and parser. See | |||
79 | - `grammar.ron` RON description of the grammar, which is used to | 79 | - `grammar.ron` RON description of the grammar, which is used to |
80 | generate `syntax_kinds` and `ast` modules, using `cargo gen-syntax` command. | 80 | generate `syntax_kinds` and `ast` modules, using `cargo gen-syntax` command. |
81 | - `algo`: generic tree algorithms, including `walk` for O(1) stack | 81 | - `algo`: generic tree algorithms, including `walk` for O(1) stack |
82 | space tree traversal (this is cool) and `visit` for type-driven | 82 | space tree traversal (this is cool). |
83 | visiting the nodes (this is double plus cool, if you understand how | ||
84 | `Visitor` works, you understand the design of syntax trees). | ||
85 | 83 | ||
86 | Tests for ra_syntax are mostly data-driven: `test_data/parser` contains subdirectories with a bunch of `.rs` | 84 | Tests for ra_syntax are mostly data-driven: `test_data/parser` contains subdirectories with a bunch of `.rs` |
87 | (test vectors) and `.txt` files with corresponding syntax trees. During testing, we check | 85 | (test vectors) and `.txt` files with corresponding syntax trees. During testing, we check |
diff --git a/docs/user/features.md b/docs/user/features.md index 0ce8f577b..757a02838 100644 --- a/docs/user/features.md +++ b/docs/user/features.md | |||
@@ -367,9 +367,9 @@ impl VariantData { | |||
367 | 367 | ||
368 | ```rust | 368 | ```rust |
369 | // before: | 369 | // before: |
370 | use algo:<|>:visitor::{Visitor, visit}; | 370 | use crate:<|>:db::{RootDatabase, FileSymbol}; |
371 | // after: | 371 | // after: |
372 | use algo::{<|>visitor::{Visitor, visit}}; | 372 | use crate::{<|>db::{RootDatabase, FileSymbol}}; |
373 | ``` | 373 | ``` |
374 | 374 | ||
375 | - Flip binary expression | 375 | - Flip binary expression |