aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src/ast.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src/ast.rs')
-rw-r--r--crates/ra_syntax/src/ast.rs365
1 files changed, 365 insertions, 0 deletions
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
new file mode 100644
index 000000000..91c67119f
--- /dev/null
+++ b/crates/ra_syntax/src/ast.rs
@@ -0,0 +1,365 @@
1mod generated;
2
3use std::marker::PhantomData;
4use std::string::String as RustString;
5
6use itertools::Itertools;
7
8pub use self::generated::*;
9use crate::{
10 yellow::{RefRoot, SyntaxNodeChildren},
11 SmolStr,
12 SyntaxKind::*,
13 SyntaxNodeRef,
14};
15
16/// The main trait to go from untyped `SyntaxNode` to a typed ast. The
17/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
18/// the same representation: a pointer to the tree root and a pointer to the
19/// node itself.
20pub trait AstNode<'a>: Clone + Copy + 'a {
21 fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self>
22 where
23 Self: Sized;
24 fn syntax(self) -> SyntaxNodeRef<'a>;
25}
26
27pub trait NameOwner<'a>: AstNode<'a> {
28 fn name(self) -> Option<Name<'a>> {
29 child_opt(self)
30 }
31}
32
33pub trait LoopBodyOwner<'a>: AstNode<'a> {
34 fn loop_body(self) -> Option<Block<'a>> {
35 child_opt(self)
36 }
37}
38
39pub trait ArgListOwner<'a>: AstNode<'a> {
40 fn arg_list(self) -> Option<ArgList<'a>> {
41 child_opt(self)
42 }
43}
44
45pub trait FnDefOwner<'a>: AstNode<'a> {
46 fn functions(self) -> AstChildren<'a, FnDef<'a>> {
47 children(self)
48 }
49}
50
51pub trait ModuleItemOwner<'a>: AstNode<'a> {
52 fn items(self) -> AstChildren<'a, ModuleItem<'a>> {
53 children(self)
54 }
55}
56
57pub trait TypeParamsOwner<'a>: AstNode<'a> {
58 fn type_param_list(self) -> Option<TypeParamList<'a>> {
59 child_opt(self)
60 }
61
62 fn where_clause(self) -> Option<WhereClause<'a>> {
63 child_opt(self)
64 }
65}
66
67pub trait AttrsOwner<'a>: AstNode<'a> {
68 fn attrs(self) -> AstChildren<'a, Attr<'a>> {
69 children(self)
70 }
71}
72
73pub trait DocCommentsOwner<'a>: AstNode<'a> {
74 fn doc_comments(self) -> AstChildren<'a, Comment<'a>> {
75 children(self)
76 }
77
78 /// Returns the textual content of a doc comment block as a single string.
79 /// That is, strips leading `///` and joins lines
80 fn doc_comment_text(self) -> RustString {
81 self.doc_comments()
82 .map(|comment| {
83 let prefix = comment.prefix();
84 let trimmed = comment
85 .text()
86 .as_str()
87 .trim()
88 .trim_start_matches(prefix)
89 .trim_start();
90 trimmed.to_owned()
91 })
92 .join("\n")
93 }
94}
95
96impl<'a> FnDef<'a> {
97 pub fn has_atom_attr(&self, atom: &str) -> bool {
98 self.attrs().filter_map(|x| x.as_atom()).any(|x| x == atom)
99 }
100}
101
102impl<'a> Attr<'a> {
103 pub fn as_atom(&self) -> Option<SmolStr> {
104 let tt = self.value()?;
105 let (_bra, attr, _ket) = tt.syntax().children().collect_tuple()?;
106 if attr.kind() == IDENT {
107 Some(attr.leaf_text().unwrap().clone())
108 } else {
109 None
110 }
111 }
112
113 pub fn as_call(&self) -> Option<(SmolStr, TokenTree<'a>)> {
114 let tt = self.value()?;
115 let (_bra, attr, args, _ket) = tt.syntax().children().collect_tuple()?;
116 let args = TokenTree::cast(args)?;
117 if attr.kind() == IDENT {
118 Some((attr.leaf_text().unwrap().clone(), args))
119 } else {
120 None
121 }
122 }
123}
124
125impl<'a> Lifetime<'a> {
126 pub fn text(&self) -> SmolStr {
127 self.syntax().leaf_text().unwrap().clone()
128 }
129}
130
131impl<'a> Char<'a> {
132 pub fn text(&self) -> &SmolStr {
133 &self.syntax().leaf_text().unwrap()
134 }
135}
136
137impl<'a> Byte<'a> {
138 pub fn text(&self) -> &SmolStr {
139 &self.syntax().leaf_text().unwrap()
140 }
141}
142
143impl<'a> ByteString<'a> {
144 pub fn text(&self) -> &SmolStr {
145 &self.syntax().leaf_text().unwrap()
146 }
147}
148
149impl<'a> String<'a> {
150 pub fn text(&self) -> &SmolStr {
151 &self.syntax().leaf_text().unwrap()
152 }
153}
154
155impl<'a> Comment<'a> {
156 pub fn text(&self) -> &SmolStr {
157 self.syntax().leaf_text().unwrap()
158 }
159
160 pub fn flavor(&self) -> CommentFlavor {
161 let text = self.text();
162 if text.starts_with("///") {
163 CommentFlavor::Doc
164 } else if text.starts_with("//!") {
165 CommentFlavor::ModuleDoc
166 } else if text.starts_with("//") {
167 CommentFlavor::Line
168 } else {
169 CommentFlavor::Multiline
170 }
171 }
172
173 pub fn prefix(&self) -> &'static str {
174 self.flavor().prefix()
175 }
176
177 pub fn count_newlines_lazy(&self) -> impl Iterator<Item = &()> {
178 self.text().chars().filter(|&c| c == '\n').map(|_| &())
179 }
180
181 pub fn has_newlines(&self) -> bool {
182 self.count_newlines_lazy().count() > 0
183 }
184}
185
186#[derive(Debug, PartialEq, Eq)]
187pub enum CommentFlavor {
188 Line,
189 Doc,
190 ModuleDoc,
191 Multiline,
192}
193
194impl CommentFlavor {
195 pub fn prefix(&self) -> &'static str {
196 use self::CommentFlavor::*;
197 match *self {
198 Line => "//",
199 Doc => "///",
200 ModuleDoc => "//!",
201 Multiline => "/*",
202 }
203 }
204}
205
206impl<'a> Whitespace<'a> {
207 pub fn text(&self) -> &SmolStr {
208 &self.syntax().leaf_text().unwrap()
209 }
210
211 pub fn count_newlines_lazy(&self) -> impl Iterator<Item = &()> {
212 self.text().chars().filter(|&c| c == '\n').map(|_| &())
213 }
214
215 pub fn has_newlines(&self) -> bool {
216 self.count_newlines_lazy().count() > 0
217 }
218}
219
220impl<'a> Name<'a> {
221 pub fn text(&self) -> SmolStr {
222 let ident = self.syntax().first_child().unwrap();
223 ident.leaf_text().unwrap().clone()
224 }
225}
226
227impl<'a> NameRef<'a> {
228 pub fn text(&self) -> SmolStr {
229 let ident = self.syntax().first_child().unwrap();
230 ident.leaf_text().unwrap().clone()
231 }
232}
233
234impl<'a> ImplItem<'a> {
235 pub fn target_type(self) -> Option<TypeRef<'a>> {
236 match self.target() {
237 (Some(t), None) | (_, Some(t)) => Some(t),
238 _ => None,
239 }
240 }
241
242 pub fn target_trait(self) -> Option<TypeRef<'a>> {
243 match self.target() {
244 (Some(t), Some(_)) => Some(t),
245 _ => None,
246 }
247 }
248
249 fn target(self) -> (Option<TypeRef<'a>>, Option<TypeRef<'a>>) {
250 let mut types = children(self);
251 let first = types.next();
252 let second = types.next();
253 (first, second)
254 }
255}
256
257impl<'a> Module<'a> {
258 pub fn has_semi(self) -> bool {
259 match self.syntax().last_child() {
260 None => false,
261 Some(node) => node.kind() == SEMI,
262 }
263 }
264}
265
266impl<'a> LetStmt<'a> {
267 pub fn has_semi(self) -> bool {
268 match self.syntax().last_child() {
269 None => false,
270 Some(node) => node.kind() == SEMI,
271 }
272 }
273}
274
275impl<'a> IfExpr<'a> {
276 pub fn then_branch(self) -> Option<Block<'a>> {
277 self.blocks().nth(0)
278 }
279 pub fn else_branch(self) -> Option<Block<'a>> {
280 self.blocks().nth(1)
281 }
282 fn blocks(self) -> AstChildren<'a, Block<'a>> {
283 children(self)
284 }
285}
286
287#[derive(Debug, Clone, Copy)]
288pub enum PathSegmentKind<'a> {
289 Name(NameRef<'a>),
290 SelfKw,
291 SuperKw,
292 CrateKw,
293}
294
295impl<'a> PathSegment<'a> {
296 pub fn parent_path(self) -> Path<'a> {
297 self.syntax()
298 .parent()
299 .and_then(Path::cast)
300 .expect("segments are always nested in paths")
301 }
302
303 pub fn kind(self) -> Option<PathSegmentKind<'a>> {
304 let res = if let Some(name_ref) = self.name_ref() {
305 PathSegmentKind::Name(name_ref)
306 } else {
307 match self.syntax().first_child()?.kind() {
308 SELF_KW => PathSegmentKind::SelfKw,
309 SUPER_KW => PathSegmentKind::SuperKw,
310 CRATE_KW => PathSegmentKind::CrateKw,
311 _ => return None,
312 }
313 };
314 Some(res)
315 }
316}
317
318impl<'a> UseTree<'a> {
319 pub fn has_star(self) -> bool {
320 self.syntax().children().any(|it| it.kind() == STAR)
321 }
322}
323
324impl<'a> UseTreeList<'a> {
325 pub fn parent_use_tree(self) -> UseTree<'a> {
326 self.syntax()
327 .parent()
328 .and_then(UseTree::cast)
329 .expect("UseTreeLists are always nested in UseTrees")
330 }
331}
332
333fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option<C> {
334 children(parent).next()
335}
336
337fn children<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> AstChildren<'a, C> {
338 AstChildren::new(parent.syntax())
339}
340
341#[derive(Debug)]
342pub struct AstChildren<'a, N> {
343 inner: SyntaxNodeChildren<RefRoot<'a>>,
344 ph: PhantomData<N>,
345}
346
347impl<'a, N> AstChildren<'a, N> {
348 fn new(parent: SyntaxNodeRef<'a>) -> Self {
349 AstChildren {
350 inner: parent.children(),
351 ph: PhantomData,
352 }
353 }
354}
355
356impl<'a, N: AstNode<'a>> Iterator for AstChildren<'a, N> {
357 type Item = N;
358 fn next(&mut self) -> Option<N> {
359 loop {
360 if let Some(n) = N::cast(self.inner.next()?) {
361 return Some(n);
362 }
363 }
364 }
365}