aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r--crates/ra_syntax/src/algo.rs2
-rw-r--r--crates/ra_syntax/src/algo/visit.rs112
-rw-r--r--crates/ra_syntax/src/lib.rs31
-rw-r--r--crates/ra_syntax/src/validation.rs18
4 files changed, 31 insertions, 132 deletions
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
3pub mod visit;
4
5use std::ops::RangeInclusive; 3use std::ops::RangeInclusive;
6 4
7use itertools::Itertools; 5use 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
3use crate::{AstNode, SyntaxNode};
4
5use std::marker::PhantomData;
6
7pub fn visitor<'a, T>() -> impl Visitor<'a, Output = T> {
8 EmptyVisitor { ph: PhantomData }
9}
10
11pub fn visitor_ctx<'a, T, C>(ctx: C) -> impl VisitorCtx<'a, Output = T, Ctx = C> {
12 EmptyVisitorCtx { ph: PhantomData, ctx }
13}
14
15pub 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
27pub 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)]
41struct EmptyVisitor<T> {
42 ph: PhantomData<fn() -> T>,
43}
44
45impl<'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)]
54struct EmptyVisitorCtx<T, C> {
55 ctx: C,
56 ph: PhantomData<fn() -> T>,
57}
58
59impl<'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)]
69pub struct Vis<V, N, F> {
70 inner: V,
71 f: F,
72 ph: PhantomData<fn(N)>,
73}
74
75impl<'a, V, N, F> Visitor<'a> for Vis<V, N, F>
76where
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)]
90pub struct VisCtx<V, N, F> {
91 inner: V,
92 f: F,
93 ph: PhantomData<fn(N)>,
94}
95
96impl<'a, V, N, F> VisitorCtx<'a> for VisCtx<V, N, F>
97where
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]
164macro_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;
5use rustc_lexer::unescape; 5use rustc_lexer::unescape;
6 6
7use crate::{ 7use 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 {
97pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { 96pub(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}