diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-07-19 12:15:55 +0100 |
---|---|---|
committer | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-07-19 12:15:55 +0100 |
commit | f209843e31af7f0e0212aa28ffec2efad2a70c6f (patch) | |
tree | 548227da78a3bea644f57714d075410c0bdf7469 /crates/ra_syntax/src/lib.rs | |
parent | 58d4983ba5745975446d60f2886d96f8d2adf0f2 (diff) | |
parent | d4a66166c002f0a49e41d856a49cb5685ac93202 (diff) |
Merge #1545
1545: migrate ra_syntax to the new rowan API r=matklad a=matklad
Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_syntax/src/lib.rs')
-rw-r--r-- | crates/ra_syntax/src/lib.rs | 122 |
1 files changed, 58 insertions, 64 deletions
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index 534c206a6..2dca4e3e8 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs | |||
@@ -31,7 +31,7 @@ pub mod ast; | |||
31 | #[doc(hidden)] | 31 | #[doc(hidden)] |
32 | pub mod fuzz; | 32 | pub mod fuzz; |
33 | 33 | ||
34 | use std::{fmt::Write, sync::Arc}; | 34 | use std::{fmt::Write, marker::PhantomData, sync::Arc}; |
35 | 35 | ||
36 | use ra_text_edit::AtomTextEdit; | 36 | use ra_text_edit::AtomTextEdit; |
37 | 37 | ||
@@ -43,8 +43,8 @@ pub use crate::{ | |||
43 | ptr::{AstPtr, SyntaxNodePtr}, | 43 | ptr::{AstPtr, SyntaxNodePtr}, |
44 | syntax_error::{Location, SyntaxError, SyntaxErrorKind}, | 44 | syntax_error::{Location, SyntaxError, SyntaxErrorKind}, |
45 | syntax_node::{ | 45 | syntax_node::{ |
46 | Direction, InsertPosition, SyntaxElement, SyntaxNode, SyntaxNodeWrapper, SyntaxToken, | 46 | Direction, InsertPosition, SyntaxElement, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, |
47 | SyntaxTreeBuilder, TreeArc, WalkEvent, | 47 | WalkEvent, |
48 | }, | 48 | }, |
49 | syntax_text::SyntaxText, | 49 | syntax_text::SyntaxText, |
50 | }; | 50 | }; |
@@ -58,48 +58,63 @@ pub use rowan::{SmolStr, TextRange, TextUnit}; | |||
58 | /// Note that we always produce a syntax tree, even for completely invalid | 58 | /// Note that we always produce a syntax tree, even for completely invalid |
59 | /// files. | 59 | /// files. |
60 | #[derive(Debug, PartialEq, Eq)] | 60 | #[derive(Debug, PartialEq, Eq)] |
61 | pub struct Parse<T: SyntaxNodeWrapper> { | 61 | pub struct Parse<T> { |
62 | tree: TreeArc<T>, | 62 | green: GreenNode, |
63 | errors: Arc<Vec<SyntaxError>>, | 63 | errors: Arc<Vec<SyntaxError>>, |
64 | _ty: PhantomData<fn() -> T>, | ||
64 | } | 65 | } |
65 | 66 | ||
66 | impl<T: SyntaxNodeWrapper> Clone for Parse<T> { | 67 | impl<T> Clone for Parse<T> { |
67 | fn clone(&self) -> Parse<T> { | 68 | fn clone(&self) -> Parse<T> { |
68 | Parse { tree: self.tree.clone(), errors: self.errors.clone() } | 69 | Parse { green: self.green.clone(), errors: self.errors.clone(), _ty: PhantomData } |
69 | } | 70 | } |
70 | } | 71 | } |
71 | 72 | ||
72 | impl<T: SyntaxNodeWrapper> Parse<T> { | 73 | impl<T> Parse<T> { |
73 | fn new(tree: TreeArc<T>, errors: Vec<SyntaxError>) -> Parse<T> { | 74 | fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> { |
74 | Parse { tree, errors: Arc::new(errors) } | 75 | Parse { green, errors: Arc::new(errors), _ty: PhantomData } |
75 | } | 76 | } |
76 | 77 | ||
77 | pub fn tree(&self) -> &T { | 78 | pub fn syntax_node(&self) -> SyntaxNode { |
78 | &*self.tree | 79 | SyntaxNode::new(self.green.clone()) |
80 | } | ||
81 | } | ||
82 | |||
83 | impl<T: AstNode> Parse<T> { | ||
84 | pub fn to_syntax(self) -> Parse<SyntaxNode> { | ||
85 | Parse { green: self.green, errors: self.errors, _ty: PhantomData } | ||
86 | } | ||
87 | |||
88 | pub fn tree(&self) -> T { | ||
89 | T::cast(self.syntax_node()).unwrap() | ||
79 | } | 90 | } |
80 | 91 | ||
81 | pub fn errors(&self) -> &[SyntaxError] { | 92 | pub fn errors(&self) -> &[SyntaxError] { |
82 | &*self.errors | 93 | &*self.errors |
83 | } | 94 | } |
84 | 95 | ||
85 | pub fn ok(self) -> Result<TreeArc<T>, Arc<Vec<SyntaxError>>> { | 96 | pub fn ok(self) -> Result<T, Arc<Vec<SyntaxError>>> { |
86 | if self.errors.is_empty() { | 97 | if self.errors.is_empty() { |
87 | Ok(self.tree) | 98 | Ok(self.tree()) |
88 | } else { | 99 | } else { |
89 | Err(self.errors) | 100 | Err(self.errors) |
90 | } | 101 | } |
91 | } | 102 | } |
92 | } | 103 | } |
93 | 104 | ||
94 | impl<T: AstNode> Parse<T> { | 105 | impl Parse<SyntaxNode> { |
95 | pub fn to_syntax(this: Self) -> Parse<SyntaxNode> { | 106 | pub fn cast<N: AstNode>(self) -> Option<Parse<N>> { |
96 | Parse { tree: this.tree().syntax().to_owned(), errors: this.errors } | 107 | if N::cast(self.syntax_node()).is_some() { |
108 | Some(Parse { green: self.green, errors: self.errors, _ty: PhantomData }) | ||
109 | } else { | ||
110 | None | ||
111 | } | ||
97 | } | 112 | } |
98 | } | 113 | } |
99 | 114 | ||
100 | impl Parse<SourceFile> { | 115 | impl Parse<SourceFile> { |
101 | pub fn debug_dump(&self) -> String { | 116 | pub fn debug_dump(&self) -> String { |
102 | let mut buf = self.tree.syntax().debug_dump(); | 117 | let mut buf = self.tree().syntax().debug_dump(); |
103 | for err in self.errors.iter() { | 118 | for err in self.errors.iter() { |
104 | writeln!(buf, "error {:?}: {}", err.location(), err.kind()).unwrap(); | 119 | writeln!(buf, "error {:?}: {}", err.location(), err.kind()).unwrap(); |
105 | } | 120 | } |
@@ -112,45 +127,38 @@ impl Parse<SourceFile> { | |||
112 | 127 | ||
113 | fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<Parse<SourceFile>> { | 128 | fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<Parse<SourceFile>> { |
114 | // FIXME: validation errors are not handled here | 129 | // FIXME: validation errors are not handled here |
115 | parsing::incremental_reparse(self.tree.syntax(), edit, self.errors.to_vec()).map( | 130 | parsing::incremental_reparse(self.tree().syntax(), edit, self.errors.to_vec()).map( |
116 | |(green_node, errors, _reparsed_range)| Parse { | 131 | |(green_node, errors, _reparsed_range)| Parse { |
117 | tree: SourceFile::new(green_node), | 132 | green: green_node, |
118 | errors: Arc::new(errors), | 133 | errors: Arc::new(errors), |
134 | _ty: PhantomData, | ||
119 | }, | 135 | }, |
120 | ) | 136 | ) |
121 | } | 137 | } |
122 | 138 | ||
123 | fn full_reparse(&self, edit: &AtomTextEdit) -> Parse<SourceFile> { | 139 | fn full_reparse(&self, edit: &AtomTextEdit) -> Parse<SourceFile> { |
124 | let text = edit.apply(self.tree.syntax().text().to_string()); | 140 | let text = edit.apply(self.tree().syntax().text().to_string()); |
125 | SourceFile::parse(&text) | 141 | SourceFile::parse(&text) |
126 | } | 142 | } |
127 | } | 143 | } |
128 | 144 | ||
129 | impl Parse<SyntaxNode> { | ||
130 | pub fn cast<T: AstNode>(self) -> Option<Parse<T>> { | ||
131 | let node = T::cast(&self.tree)?; | ||
132 | Some(Parse { tree: node.to_owned(), errors: self.errors }) | ||
133 | } | ||
134 | } | ||
135 | |||
136 | /// `SourceFile` represents a parse tree for a single Rust file. | 145 | /// `SourceFile` represents a parse tree for a single Rust file. |
137 | pub use crate::ast::SourceFile; | 146 | pub use crate::ast::SourceFile; |
138 | 147 | ||
139 | impl SourceFile { | 148 | impl SourceFile { |
140 | fn new(green: GreenNode) -> TreeArc<SourceFile> { | 149 | fn new(green: GreenNode) -> SourceFile { |
141 | let root = SyntaxNode::new(green); | 150 | let root = SyntaxNode::new(green); |
142 | if cfg!(debug_assertions) { | 151 | if cfg!(debug_assertions) { |
143 | validation::validate_block_structure(&root); | 152 | validation::validate_block_structure(&root); |
144 | } | 153 | } |
145 | assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); | 154 | assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE); |
146 | TreeArc::cast(root) | 155 | SourceFile::cast(root).unwrap() |
147 | } | 156 | } |
148 | 157 | ||
149 | pub fn parse(text: &str) -> Parse<SourceFile> { | 158 | pub fn parse(text: &str) -> Parse<SourceFile> { |
150 | let (green, mut errors) = parsing::parse_text(text); | 159 | let (green, mut errors) = parsing::parse_text(text); |
151 | let tree = SourceFile::new(green); | 160 | errors.extend(validation::validate(&SourceFile::new(green.clone()))); |
152 | errors.extend(validation::validate(&tree)); | 161 | Parse { green, errors: Arc::new(errors), _ty: PhantomData } |
153 | Parse { tree, errors: Arc::new(errors) } | ||
154 | } | 162 | } |
155 | } | 163 | } |
156 | 164 | ||
@@ -170,14 +178,14 @@ fn api_walkthrough() { | |||
170 | // The `parse` method returns a `Parse` -- a pair of syntax tree and a list | 178 | // The `parse` method returns a `Parse` -- a pair of syntax tree and a list |
171 | // of errors. That is, syntax tree is constructed even in presence of errors. | 179 | // of errors. That is, syntax tree is constructed even in presence of errors. |
172 | let parse = SourceFile::parse(source_code); | 180 | let parse = SourceFile::parse(source_code); |
173 | assert!(parse.errors.is_empty()); | 181 | assert!(parse.errors().is_empty()); |
174 | 182 | ||
175 | // Due to the way ownership is set up, owned syntax Nodes always live behind | 183 | // The `tree` method returns an owned syntax node of type `SourceFile`. |
176 | // a `TreeArc` smart pointer. `TreeArc` is roughly an `std::sync::Arc` which | 184 | // Owned nodes are cheap: inside, they are `Rc` handles to the underling data. |
177 | // points to the whole file instead of an individual node. | 185 | let file: SourceFile = parse.tree(); |
178 | let file: TreeArc<SourceFile> = parse.tree; | ||
179 | 186 | ||
180 | // `SourceFile` is the root of the syntax tree. We can iterate file's items: | 187 | // `SourceFile` is the root of the syntax tree. We can iterate file's items. |
188 | // Let's fetch the `foo` function. | ||
181 | let mut func = None; | 189 | let mut func = None; |
182 | for item in file.items() { | 190 | for item in file.items() { |
183 | match item.kind() { | 191 | match item.kind() { |
@@ -185,31 +193,26 @@ fn api_walkthrough() { | |||
185 | _ => unreachable!(), | 193 | _ => unreachable!(), |
186 | } | 194 | } |
187 | } | 195 | } |
188 | // The returned items are always references. | 196 | let func: ast::FnDef = func.unwrap(); |
189 | let func: &ast::FnDef = func.unwrap(); | ||
190 | |||
191 | // All nodes implement `ToOwned` trait, with `Owned = TreeArc<Self>`. | ||
192 | // `to_owned` is a cheap operation: atomic increment. | ||
193 | let _owned_func: TreeArc<ast::FnDef> = func.to_owned(); | ||
194 | 197 | ||
195 | // Each AST node has a bunch of getters for children. All getters return | 198 | // Each AST node has a bunch of getters for children. All getters return |
196 | // `Option`s though, to account for incomplete code. Some getters are common | 199 | // `Option`s though, to account for incomplete code. Some getters are common |
197 | // for several kinds of node. In this case, a trait like `ast::NameOwner` | 200 | // for several kinds of node. In this case, a trait like `ast::NameOwner` |
198 | // usually exists. By convention, all ast types should be used with `ast::` | 201 | // usually exists. By convention, all ast types should be used with `ast::` |
199 | // qualifier. | 202 | // qualifier. |
200 | let name: Option<&ast::Name> = func.name(); | 203 | let name: Option<ast::Name> = func.name(); |
201 | let name = name.unwrap(); | 204 | let name = name.unwrap(); |
202 | assert_eq!(name.text(), "foo"); | 205 | assert_eq!(name.text(), "foo"); |
203 | 206 | ||
204 | // Let's get the `1 + 1` expression! | 207 | // Let's get the `1 + 1` expression! |
205 | let block: &ast::Block = func.body().unwrap(); | 208 | let block: ast::Block = func.body().unwrap(); |
206 | let expr: &ast::Expr = block.expr().unwrap(); | 209 | let expr: ast::Expr = block.expr().unwrap(); |
207 | 210 | ||
208 | // "Enum"-like nodes are represented using the "kind" pattern. It allows us | 211 | // "Enum"-like nodes are represented using the "kind" pattern. It allows us |
209 | // to match exhaustively against all flavors of nodes, while maintaining | 212 | // to match exhaustively against all flavors of nodes, while maintaining |
210 | // internal representation flexibility. The drawback is that one can't write | 213 | // internal representation flexibility. The drawback is that one can't write |
211 | // nested matches as one pattern. | 214 | // nested matches as one pattern. |
212 | let bin_expr: &ast::BinExpr = match expr.kind() { | 215 | let bin_expr: ast::BinExpr = match expr.kind() { |
213 | ast::ExprKind::BinExpr(e) => e, | 216 | ast::ExprKind::BinExpr(e) => e, |
214 | _ => unreachable!(), | 217 | _ => unreachable!(), |
215 | }; | 218 | }; |
@@ -219,23 +222,14 @@ fn api_walkthrough() { | |||
219 | let expr_syntax: &SyntaxNode = expr.syntax(); | 222 | let expr_syntax: &SyntaxNode = expr.syntax(); |
220 | 223 | ||
221 | // Note how `expr` and `bin_expr` are in fact the same node underneath: | 224 | // Note how `expr` and `bin_expr` are in fact the same node underneath: |
222 | assert!(std::ptr::eq(expr_syntax, bin_expr.syntax())); | 225 | assert!(expr_syntax == bin_expr.syntax()); |
223 | 226 | ||
224 | // To go from CST to AST, `AstNode::cast` function is used: | 227 | // To go from CST to AST, `AstNode::cast` function is used: |
225 | let expr = match ast::Expr::cast(expr_syntax) { | 228 | let _expr: ast::Expr = match ast::Expr::cast(expr_syntax.clone()) { |
226 | Some(e) => e, | 229 | Some(e) => e, |
227 | None => unreachable!(), | 230 | None => unreachable!(), |
228 | }; | 231 | }; |
229 | 232 | ||
230 | // Note how expr is also a reference! | ||
231 | let expr: &ast::Expr = expr; | ||
232 | |||
233 | // This is possible because the underlying representation is the same: | ||
234 | assert_eq!( | ||
235 | expr as *const ast::Expr as *const u8, | ||
236 | expr_syntax as *const SyntaxNode as *const u8 | ||
237 | ); | ||
238 | |||
239 | // The two properties each syntax node has is a `SyntaxKind`: | 233 | // The two properties each syntax node has is a `SyntaxKind`: |
240 | assert_eq!(expr_syntax.kind(), SyntaxKind::BIN_EXPR); | 234 | assert_eq!(expr_syntax.kind(), SyntaxKind::BIN_EXPR); |
241 | 235 | ||
@@ -248,7 +242,7 @@ fn api_walkthrough() { | |||
248 | assert_eq!(text.to_string(), "1 + 1"); | 242 | assert_eq!(text.to_string(), "1 + 1"); |
249 | 243 | ||
250 | // There's a bunch of traversal methods on `SyntaxNode`: | 244 | // There's a bunch of traversal methods on `SyntaxNode`: |
251 | assert_eq!(expr_syntax.parent(), Some(block.syntax())); | 245 | assert_eq!(expr_syntax.parent().as_ref(), Some(block.syntax())); |
252 | assert_eq!(block.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{'])); | 246 | assert_eq!(block.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{'])); |
253 | assert_eq!( | 247 | assert_eq!( |
254 | expr_syntax.next_sibling_or_token().map(|it| it.kind()), | 248 | expr_syntax.next_sibling_or_token().map(|it| it.kind()), |
@@ -257,7 +251,7 @@ fn api_walkthrough() { | |||
257 | 251 | ||
258 | // As well as some iterator helpers: | 252 | // As well as some iterator helpers: |
259 | let f = expr_syntax.ancestors().find_map(ast::FnDef::cast); | 253 | let f = expr_syntax.ancestors().find_map(ast::FnDef::cast); |
260 | assert_eq!(f, Some(&*func)); | 254 | assert_eq!(f, Some(func)); |
261 | assert!(expr_syntax.siblings_with_tokens(Direction::Next).any(|it| it.kind() == T!['}'])); | 255 | assert!(expr_syntax.siblings_with_tokens(Direction::Next).any(|it| it.kind() == T!['}'])); |
262 | assert_eq!( | 256 | assert_eq!( |
263 | expr_syntax.descendants_with_tokens().count(), | 257 | expr_syntax.descendants_with_tokens().count(), |
@@ -272,7 +266,7 @@ fn api_walkthrough() { | |||
272 | for event in expr_syntax.preorder_with_tokens() { | 266 | for event in expr_syntax.preorder_with_tokens() { |
273 | match event { | 267 | match event { |
274 | WalkEvent::Enter(node) => { | 268 | WalkEvent::Enter(node) => { |
275 | let text = match node { | 269 | let text = match &node { |
276 | SyntaxElement::Node(it) => it.text().to_string(), | 270 | SyntaxElement::Node(it) => it.text().to_string(), |
277 | SyntaxElement::Token(it) => it.text().to_string(), | 271 | SyntaxElement::Token(it) => it.text().to_string(), |
278 | }; | 272 | }; |
@@ -319,7 +313,7 @@ fn api_walkthrough() { | |||
319 | let mut exprs_visit = Vec::new(); | 313 | let mut exprs_visit = Vec::new(); |
320 | for node in file.syntax().descendants() { | 314 | for node in file.syntax().descendants() { |
321 | if let Some(result) = | 315 | if let Some(result) = |
322 | visitor().visit::<ast::Expr, _>(|expr| expr.syntax().text().to_string()).accept(node) | 316 | visitor().visit::<ast::Expr, _>(|expr| expr.syntax().text().to_string()).accept(&node) |
323 | { | 317 | { |
324 | exprs_visit.push(result); | 318 | exprs_visit.push(result); |
325 | } | 319 | } |