diff options
Diffstat (limited to 'crates/ra_syntax/src/ast.rs')
-rw-r--r-- | crates/ra_syntax/src/ast.rs | 802 |
1 files changed, 95 insertions, 707 deletions
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index a6fac07c4..9f5c41b0c 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -1,15 +1,24 @@ | |||
1 | //! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s | 1 | //! Abstract Syntax Tree, layered on top of untyped `SyntaxNode`s |
2 | |||
2 | mod generated; | 3 | mod generated; |
4 | mod traits; | ||
5 | mod tokens; | ||
6 | mod extensions; | ||
7 | mod expr_extensions; | ||
3 | 8 | ||
4 | use std::marker::PhantomData; | 9 | use std::marker::PhantomData; |
5 | 10 | ||
6 | use itertools::Itertools; | ||
7 | |||
8 | pub use self::generated::*; | ||
9 | use crate::{ | 11 | use crate::{ |
10 | syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes}, | 12 | syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken}, |
11 | SmolStr, | 13 | SmolStr, |
12 | SyntaxKind::*, | 14 | }; |
15 | |||
16 | pub use self::{ | ||
17 | generated::*, | ||
18 | traits::*, | ||
19 | tokens::*, | ||
20 | extensions::{PathSegmentKind, StructKind, SelfParamKind}, | ||
21 | expr_extensions::{ElseBranch, PrefixOp, BinOp, LiteralKind}, | ||
13 | }; | 22 | }; |
14 | 23 | ||
15 | /// The main trait to go from untyped `SyntaxNode` to a typed ast. The | 24 | /// The main trait to go from untyped `SyntaxNode` to a typed ast. The |
@@ -25,412 +34,18 @@ pub trait AstNode: | |||
25 | fn syntax(&self) -> &SyntaxNode; | 34 | fn syntax(&self) -> &SyntaxNode; |
26 | } | 35 | } |
27 | 36 | ||
28 | pub trait AstToken: AstNode { | 37 | /// Like `AstNode`, but wraps tokens rather than interior nodes. |
29 | fn text(&self) -> &SmolStr { | 38 | pub trait AstToken<'a> { |
30 | self.syntax().leaf_text().unwrap() | 39 | fn cast(token: SyntaxToken<'a>) -> Option<Self> |
31 | } | 40 | where |
32 | } | 41 | Self: Sized; |
33 | 42 | fn syntax(&self) -> SyntaxToken<'a>; | |
34 | pub trait TypeAscriptionOwner: AstNode { | 43 | fn text(&self) -> &'a SmolStr { |
35 | fn ascribed_type(&self) -> Option<&TypeRef> { | 44 | self.syntax().text() |
36 | child_opt(self) | ||
37 | } | ||
38 | } | ||
39 | |||
40 | pub trait NameOwner: AstNode { | ||
41 | fn name(&self) -> Option<&Name> { | ||
42 | child_opt(self) | ||
43 | } | ||
44 | } | ||
45 | |||
46 | pub trait VisibilityOwner: AstNode { | ||
47 | fn visibility(&self) -> Option<&Visibility> { | ||
48 | child_opt(self) | ||
49 | } | ||
50 | } | ||
51 | |||
52 | pub trait LoopBodyOwner: AstNode { | ||
53 | fn loop_body(&self) -> Option<&Block> { | ||
54 | child_opt(self) | ||
55 | } | ||
56 | } | ||
57 | |||
58 | pub trait ArgListOwner: AstNode { | ||
59 | fn arg_list(&self) -> Option<&ArgList> { | ||
60 | child_opt(self) | ||
61 | } | ||
62 | } | ||
63 | |||
64 | pub trait FnDefOwner: AstNode { | ||
65 | fn functions(&self) -> AstChildren<FnDef> { | ||
66 | children(self) | ||
67 | } | ||
68 | } | ||
69 | |||
70 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
71 | pub enum ItemOrMacro<'a> { | ||
72 | Item(&'a ModuleItem), | ||
73 | Macro(&'a MacroCall), | ||
74 | } | ||
75 | |||
76 | pub trait ModuleItemOwner: AstNode { | ||
77 | fn items(&self) -> AstChildren<ModuleItem> { | ||
78 | children(self) | ||
79 | } | ||
80 | fn items_with_macros(&self) -> ItemOrMacroIter { | ||
81 | ItemOrMacroIter(self.syntax().children()) | ||
82 | } | ||
83 | } | ||
84 | |||
85 | #[derive(Debug)] | ||
86 | pub struct ItemOrMacroIter<'a>(SyntaxNodeChildren<'a>); | ||
87 | |||
88 | impl<'a> Iterator for ItemOrMacroIter<'a> { | ||
89 | type Item = ItemOrMacro<'a>; | ||
90 | fn next(&mut self) -> Option<ItemOrMacro<'a>> { | ||
91 | loop { | ||
92 | let n = self.0.next()?; | ||
93 | if let Some(item) = ModuleItem::cast(n) { | ||
94 | return Some(ItemOrMacro::Item(item)); | ||
95 | } | ||
96 | if let Some(call) = MacroCall::cast(n) { | ||
97 | return Some(ItemOrMacro::Macro(call)); | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | } | ||
102 | |||
103 | pub trait TypeParamsOwner: AstNode { | ||
104 | fn type_param_list(&self) -> Option<&TypeParamList> { | ||
105 | child_opt(self) | ||
106 | } | ||
107 | |||
108 | fn where_clause(&self) -> Option<&WhereClause> { | ||
109 | child_opt(self) | ||
110 | } | ||
111 | } | ||
112 | |||
113 | pub trait AttrsOwner: AstNode { | ||
114 | fn attrs(&self) -> AstChildren<Attr> { | ||
115 | children(self) | ||
116 | } | ||
117 | fn has_atom_attr(&self, atom: &str) -> bool { | ||
118 | self.attrs().filter_map(|x| x.as_atom()).any(|x| x == atom) | ||
119 | } | ||
120 | } | ||
121 | |||
122 | pub trait DocCommentsOwner: AstNode { | ||
123 | fn doc_comments(&self) -> AstChildren<Comment> { | ||
124 | children(self) | ||
125 | } | ||
126 | |||
127 | /// Returns the textual content of a doc comment block as a single string. | ||
128 | /// That is, strips leading `///` (+ optional 1 character of whitespace) | ||
129 | /// and joins lines. | ||
130 | fn doc_comment_text(&self) -> Option<std::string::String> { | ||
131 | let docs = self | ||
132 | .doc_comments() | ||
133 | .filter(|comment| comment.is_doc_comment()) | ||
134 | .map(|comment| { | ||
135 | let prefix_len = comment.prefix().len(); | ||
136 | |||
137 | let line = comment.text().as_str(); | ||
138 | |||
139 | // Determine if the prefix or prefix + 1 char is stripped | ||
140 | let pos = | ||
141 | if line.chars().nth(prefix_len).map(|c| c.is_whitespace()).unwrap_or(false) { | ||
142 | prefix_len + 1 | ||
143 | } else { | ||
144 | prefix_len | ||
145 | }; | ||
146 | |||
147 | line[pos..].to_owned() | ||
148 | }) | ||
149 | .join("\n"); | ||
150 | |||
151 | if docs.is_empty() { | ||
152 | None | ||
153 | } else { | ||
154 | Some(docs) | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | |||
159 | impl Attr { | ||
160 | pub fn is_inner(&self) -> bool { | ||
161 | let tt = match self.value() { | ||
162 | None => return false, | ||
163 | Some(tt) => tt, | ||
164 | }; | ||
165 | |||
166 | let prev = match tt.syntax().prev_sibling() { | ||
167 | None => return false, | ||
168 | Some(prev) => prev, | ||
169 | }; | ||
170 | |||
171 | prev.kind() == EXCL | ||
172 | } | ||
173 | |||
174 | pub fn as_atom(&self) -> Option<SmolStr> { | ||
175 | let tt = self.value()?; | ||
176 | let (_bra, attr, _ket) = tt.syntax().children().collect_tuple()?; | ||
177 | if attr.kind() == IDENT { | ||
178 | Some(attr.leaf_text().unwrap().clone()) | ||
179 | } else { | ||
180 | None | ||
181 | } | ||
182 | } | ||
183 | |||
184 | pub fn as_call(&self) -> Option<(SmolStr, &TokenTree)> { | ||
185 | let tt = self.value()?; | ||
186 | let (_bra, attr, args, _ket) = tt.syntax().children().collect_tuple()?; | ||
187 | let args = TokenTree::cast(args)?; | ||
188 | if attr.kind() == IDENT { | ||
189 | Some((attr.leaf_text().unwrap().clone(), args)) | ||
190 | } else { | ||
191 | None | ||
192 | } | ||
193 | } | ||
194 | |||
195 | pub fn as_named(&self) -> Option<SmolStr> { | ||
196 | let tt = self.value()?; | ||
197 | let attr = tt.syntax().children().nth(1)?; | ||
198 | if attr.kind() == IDENT { | ||
199 | Some(attr.leaf_text().unwrap().clone()) | ||
200 | } else { | ||
201 | None | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | |||
206 | impl Comment { | ||
207 | pub fn flavor(&self) -> CommentFlavor { | ||
208 | let text = self.text(); | ||
209 | if text.starts_with("///") { | ||
210 | CommentFlavor::Doc | ||
211 | } else if text.starts_with("//!") { | ||
212 | CommentFlavor::ModuleDoc | ||
213 | } else if text.starts_with("//") { | ||
214 | CommentFlavor::Line | ||
215 | } else { | ||
216 | CommentFlavor::Multiline | ||
217 | } | ||
218 | } | ||
219 | |||
220 | pub fn is_doc_comment(&self) -> bool { | ||
221 | self.flavor().is_doc_comment() | ||
222 | } | ||
223 | |||
224 | pub fn prefix(&self) -> &'static str { | ||
225 | self.flavor().prefix() | ||
226 | } | ||
227 | |||
228 | pub fn count_newlines_lazy(&self) -> impl Iterator<Item = &()> { | ||
229 | self.text().chars().filter(|&c| c == '\n').map(|_| &()) | ||
230 | } | ||
231 | |||
232 | pub fn has_newlines(&self) -> bool { | ||
233 | self.count_newlines_lazy().count() > 0 | ||
234 | } | ||
235 | } | ||
236 | |||
237 | #[derive(Debug, PartialEq, Eq)] | ||
238 | pub enum CommentFlavor { | ||
239 | Line, | ||
240 | Doc, | ||
241 | ModuleDoc, | ||
242 | Multiline, | ||
243 | } | ||
244 | |||
245 | impl CommentFlavor { | ||
246 | pub fn prefix(&self) -> &'static str { | ||
247 | use self::CommentFlavor::*; | ||
248 | match *self { | ||
249 | Line => "//", | ||
250 | Doc => "///", | ||
251 | ModuleDoc => "//!", | ||
252 | Multiline => "/*", | ||
253 | } | ||
254 | } | ||
255 | |||
256 | pub fn is_doc_comment(&self) -> bool { | ||
257 | match self { | ||
258 | CommentFlavor::Doc | CommentFlavor::ModuleDoc => true, | ||
259 | _ => false, | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | |||
264 | impl Whitespace { | ||
265 | pub fn count_newlines_lazy(&self) -> impl Iterator<Item = &()> { | ||
266 | self.text().chars().filter(|&c| c == '\n').map(|_| &()) | ||
267 | } | ||
268 | |||
269 | pub fn has_newlines(&self) -> bool { | ||
270 | self.text().contains('\n') | ||
271 | } | ||
272 | } | ||
273 | |||
274 | impl Name { | ||
275 | pub fn text(&self) -> &SmolStr { | ||
276 | let ident = self.syntax().first_child().unwrap(); | ||
277 | ident.leaf_text().unwrap() | ||
278 | } | ||
279 | } | ||
280 | |||
281 | impl NameRef { | ||
282 | pub fn text(&self) -> &SmolStr { | ||
283 | let ident = self.syntax().first_child().unwrap(); | ||
284 | ident.leaf_text().unwrap() | ||
285 | } | ||
286 | } | ||
287 | |||
288 | impl ImplBlock { | ||
289 | pub fn target_type(&self) -> Option<&TypeRef> { | ||
290 | match self.target() { | ||
291 | (Some(t), None) | (_, Some(t)) => Some(t), | ||
292 | _ => None, | ||
293 | } | ||
294 | } | ||
295 | |||
296 | pub fn target_trait(&self) -> Option<&TypeRef> { | ||
297 | match self.target() { | ||
298 | (Some(t), Some(_)) => Some(t), | ||
299 | _ => None, | ||
300 | } | ||
301 | } | ||
302 | |||
303 | fn target(&self) -> (Option<&TypeRef>, Option<&TypeRef>) { | ||
304 | let mut types = children(self); | ||
305 | let first = types.next(); | ||
306 | let second = types.next(); | ||
307 | (first, second) | ||
308 | } | ||
309 | } | ||
310 | |||
311 | impl Module { | ||
312 | pub fn has_semi(&self) -> bool { | ||
313 | match self.syntax().last_child() { | ||
314 | None => false, | ||
315 | Some(node) => node.kind() == SEMI, | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | |||
320 | impl LetStmt { | ||
321 | pub fn has_semi(&self) -> bool { | ||
322 | match self.syntax().last_child() { | ||
323 | None => false, | ||
324 | Some(node) => node.kind() == SEMI, | ||
325 | } | ||
326 | } | ||
327 | } | ||
328 | |||
329 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
330 | pub enum ElseBranchFlavor<'a> { | ||
331 | Block(&'a Block), | ||
332 | IfExpr(&'a IfExpr), | ||
333 | } | ||
334 | |||
335 | impl IfExpr { | ||
336 | pub fn then_branch(&self) -> Option<&Block> { | ||
337 | self.blocks().nth(0) | ||
338 | } | ||
339 | pub fn else_branch(&self) -> Option<ElseBranchFlavor> { | ||
340 | let res = match self.blocks().nth(1) { | ||
341 | Some(block) => ElseBranchFlavor::Block(block), | ||
342 | None => { | ||
343 | let elif: &IfExpr = child_opt(self)?; | ||
344 | ElseBranchFlavor::IfExpr(elif) | ||
345 | } | ||
346 | }; | ||
347 | Some(res) | ||
348 | } | ||
349 | |||
350 | fn blocks(&self) -> AstChildren<Block> { | ||
351 | children(self) | ||
352 | } | ||
353 | } | ||
354 | |||
355 | impl ExprStmt { | ||
356 | pub fn has_semi(&self) -> bool { | ||
357 | match self.syntax().last_child() { | ||
358 | None => false, | ||
359 | Some(node) => node.kind() == SEMI, | ||
360 | } | ||
361 | } | ||
362 | } | ||
363 | |||
364 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
365 | pub enum PathSegmentKind<'a> { | ||
366 | Name(&'a NameRef), | ||
367 | SelfKw, | ||
368 | SuperKw, | ||
369 | CrateKw, | ||
370 | } | ||
371 | |||
372 | impl PathSegment { | ||
373 | pub fn parent_path(&self) -> &Path { | ||
374 | self.syntax().parent().and_then(Path::cast).expect("segments are always nested in paths") | ||
375 | } | ||
376 | |||
377 | pub fn kind(&self) -> Option<PathSegmentKind> { | ||
378 | let res = if let Some(name_ref) = self.name_ref() { | ||
379 | PathSegmentKind::Name(name_ref) | ||
380 | } else { | ||
381 | match self.syntax().first_child()?.kind() { | ||
382 | SELF_KW => PathSegmentKind::SelfKw, | ||
383 | SUPER_KW => PathSegmentKind::SuperKw, | ||
384 | CRATE_KW => PathSegmentKind::CrateKw, | ||
385 | _ => return None, | ||
386 | } | ||
387 | }; | ||
388 | Some(res) | ||
389 | } | ||
390 | |||
391 | pub fn has_colon_colon(&self) -> bool { | ||
392 | match self.syntax.first_child().map(|s| s.kind()) { | ||
393 | Some(COLONCOLON) => true, | ||
394 | _ => false, | ||
395 | } | ||
396 | } | ||
397 | } | ||
398 | |||
399 | impl Path { | ||
400 | pub fn parent_path(&self) -> Option<&Path> { | ||
401 | self.syntax().parent().and_then(Path::cast) | ||
402 | } | ||
403 | } | ||
404 | |||
405 | impl UseTree { | ||
406 | pub fn has_star(&self) -> bool { | ||
407 | self.syntax().children().any(|it| it.kind() == STAR) | ||
408 | } | ||
409 | } | ||
410 | |||
411 | impl UseTreeList { | ||
412 | pub fn parent_use_tree(&self) -> &UseTree { | ||
413 | self.syntax() | ||
414 | .parent() | ||
415 | .and_then(UseTree::cast) | ||
416 | .expect("UseTreeLists are always nested in UseTrees") | ||
417 | } | ||
418 | } | ||
419 | |||
420 | impl RefPat { | ||
421 | pub fn is_mut(&self) -> bool { | ||
422 | self.syntax().children().any(|n| n.kind() == MUT_KW) | ||
423 | } | 45 | } |
424 | } | 46 | } |
425 | 47 | ||
426 | fn child_opt<P: AstNode, C: AstNode>(parent: &P) -> Option<&C> { | 48 | /// An iterator over `SyntaxNode` children of a particular AST type. |
427 | children(parent).next() | ||
428 | } | ||
429 | |||
430 | fn children<P: AstNode, C: AstNode>(parent: &P) -> AstChildren<C> { | ||
431 | AstChildren::new(parent.syntax()) | ||
432 | } | ||
433 | |||
434 | #[derive(Debug)] | 49 | #[derive(Debug)] |
435 | pub struct AstChildren<'a, N> { | 50 | pub struct AstChildren<'a, N> { |
436 | inner: SyntaxNodeChildren<'a>, | 51 | inner: SyntaxNodeChildren<'a>, |
@@ -446,310 +61,16 @@ impl<'a, N> AstChildren<'a, N> { | |||
446 | impl<'a, N: AstNode + 'a> Iterator for AstChildren<'a, N> { | 61 | impl<'a, N: AstNode + 'a> Iterator for AstChildren<'a, N> { |
447 | type Item = &'a N; | 62 | type Item = &'a N; |
448 | fn next(&mut self) -> Option<&'a N> { | 63 | fn next(&mut self) -> Option<&'a N> { |
449 | loop { | 64 | self.inner.by_ref().find_map(N::cast) |
450 | if let Some(n) = N::cast(self.inner.next()?) { | ||
451 | return Some(n); | ||
452 | } | ||
453 | } | ||
454 | } | ||
455 | } | ||
456 | |||
457 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
458 | pub enum StructFlavor<'a> { | ||
459 | Tuple(&'a PosFieldDefList), | ||
460 | Named(&'a NamedFieldDefList), | ||
461 | Unit, | ||
462 | } | ||
463 | |||
464 | impl StructFlavor<'_> { | ||
465 | fn from_node<N: AstNode>(node: &N) -> StructFlavor { | ||
466 | if let Some(nfdl) = child_opt::<_, NamedFieldDefList>(node) { | ||
467 | StructFlavor::Named(nfdl) | ||
468 | } else if let Some(pfl) = child_opt::<_, PosFieldDefList>(node) { | ||
469 | StructFlavor::Tuple(pfl) | ||
470 | } else { | ||
471 | StructFlavor::Unit | ||
472 | } | ||
473 | } | 65 | } |
474 | } | 66 | } |
475 | 67 | ||
476 | impl StructDef { | 68 | fn child_opt<P: AstNode, C: AstNode>(parent: &P) -> Option<&C> { |
477 | pub fn flavor(&self) -> StructFlavor { | 69 | children(parent).next() |
478 | StructFlavor::from_node(self) | ||
479 | } | ||
480 | } | ||
481 | |||
482 | impl EnumVariant { | ||
483 | pub fn parent_enum(&self) -> &EnumDef { | ||
484 | self.syntax() | ||
485 | .parent() | ||
486 | .and_then(|it| it.parent()) | ||
487 | .and_then(EnumDef::cast) | ||
488 | .expect("EnumVariants are always nested in Enums") | ||
489 | } | ||
490 | pub fn flavor(&self) -> StructFlavor { | ||
491 | StructFlavor::from_node(self) | ||
492 | } | ||
493 | } | ||
494 | |||
495 | impl PointerType { | ||
496 | pub fn is_mut(&self) -> bool { | ||
497 | self.syntax().children().any(|n| n.kind() == MUT_KW) | ||
498 | } | ||
499 | } | ||
500 | |||
501 | impl ReferenceType { | ||
502 | pub fn is_mut(&self) -> bool { | ||
503 | self.syntax().children().any(|n| n.kind() == MUT_KW) | ||
504 | } | ||
505 | } | ||
506 | |||
507 | impl RefExpr { | ||
508 | pub fn is_mut(&self) -> bool { | ||
509 | self.syntax().children().any(|n| n.kind() == MUT_KW) | ||
510 | } | ||
511 | } | ||
512 | |||
513 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
514 | pub enum PrefixOp { | ||
515 | /// The `*` operator for dereferencing | ||
516 | Deref, | ||
517 | /// The `!` operator for logical inversion | ||
518 | Not, | ||
519 | /// The `-` operator for negation | ||
520 | Neg, | ||
521 | } | ||
522 | |||
523 | impl PrefixExpr { | ||
524 | pub fn op_kind(&self) -> Option<PrefixOp> { | ||
525 | match self.syntax().first_child()?.kind() { | ||
526 | STAR => Some(PrefixOp::Deref), | ||
527 | EXCL => Some(PrefixOp::Not), | ||
528 | MINUS => Some(PrefixOp::Neg), | ||
529 | _ => None, | ||
530 | } | ||
531 | } | ||
532 | |||
533 | pub fn op(&self) -> Option<&SyntaxNode> { | ||
534 | self.syntax().first_child() | ||
535 | } | ||
536 | } | ||
537 | |||
538 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
539 | pub enum BinOp { | ||
540 | /// The `||` operator for boolean OR | ||
541 | BooleanOr, | ||
542 | /// The `&&` operator for boolean AND | ||
543 | BooleanAnd, | ||
544 | /// The `==` operator for equality testing | ||
545 | EqualityTest, | ||
546 | /// The `!=` operator for equality testing | ||
547 | NegatedEqualityTest, | ||
548 | /// The `<=` operator for lesser-equal testing | ||
549 | LesserEqualTest, | ||
550 | /// The `>=` operator for greater-equal testing | ||
551 | GreaterEqualTest, | ||
552 | /// The `<` operator for comparison | ||
553 | LesserTest, | ||
554 | /// The `>` operator for comparison | ||
555 | GreaterTest, | ||
556 | /// The `+` operator for addition | ||
557 | Addition, | ||
558 | /// The `*` operator for multiplication | ||
559 | Multiplication, | ||
560 | /// The `-` operator for subtraction | ||
561 | Subtraction, | ||
562 | /// The `/` operator for division | ||
563 | Division, | ||
564 | /// The `%` operator for remainder after division | ||
565 | Remainder, | ||
566 | /// The `<<` operator for left shift | ||
567 | LeftShift, | ||
568 | /// The `>>` operator for right shift | ||
569 | RightShift, | ||
570 | /// The `^` operator for bitwise XOR | ||
571 | BitwiseXor, | ||
572 | /// The `|` operator for bitwise OR | ||
573 | BitwiseOr, | ||
574 | /// The `&` operator for bitwise AND | ||
575 | BitwiseAnd, | ||
576 | /// The `..` operator for right-open ranges | ||
577 | RangeRightOpen, | ||
578 | /// The `..=` operator for right-closed ranges | ||
579 | RangeRightClosed, | ||
580 | /// The `=` operator for assignment | ||
581 | Assignment, | ||
582 | /// The `+=` operator for assignment after addition | ||
583 | AddAssign, | ||
584 | /// The `/=` operator for assignment after division | ||
585 | DivAssign, | ||
586 | /// The `*=` operator for assignment after multiplication | ||
587 | MulAssign, | ||
588 | /// The `%=` operator for assignment after remainders | ||
589 | RemAssign, | ||
590 | /// The `>>=` operator for assignment after shifting right | ||
591 | ShrAssign, | ||
592 | /// The `<<=` operator for assignment after shifting left | ||
593 | ShlAssign, | ||
594 | /// The `-=` operator for assignment after subtraction | ||
595 | SubAssign, | ||
596 | /// The `|=` operator for assignment after bitwise OR | ||
597 | BitOrAssign, | ||
598 | /// The `&=` operator for assignment after bitwise AND | ||
599 | BitAndAssign, | ||
600 | /// The `^=` operator for assignment after bitwise XOR | ||
601 | BitXorAssign, | ||
602 | } | ||
603 | |||
604 | impl BinExpr { | ||
605 | fn op_details(&self) -> Option<(&SyntaxNode, BinOp)> { | ||
606 | self.syntax().children().find_map(|c| match c.kind() { | ||
607 | PIPEPIPE => Some((c, BinOp::BooleanOr)), | ||
608 | AMPAMP => Some((c, BinOp::BooleanAnd)), | ||
609 | EQEQ => Some((c, BinOp::EqualityTest)), | ||
610 | NEQ => Some((c, BinOp::NegatedEqualityTest)), | ||
611 | LTEQ => Some((c, BinOp::LesserEqualTest)), | ||
612 | GTEQ => Some((c, BinOp::GreaterEqualTest)), | ||
613 | L_ANGLE => Some((c, BinOp::LesserTest)), | ||
614 | R_ANGLE => Some((c, BinOp::GreaterTest)), | ||
615 | PLUS => Some((c, BinOp::Addition)), | ||
616 | STAR => Some((c, BinOp::Multiplication)), | ||
617 | MINUS => Some((c, BinOp::Subtraction)), | ||
618 | SLASH => Some((c, BinOp::Division)), | ||
619 | PERCENT => Some((c, BinOp::Remainder)), | ||
620 | SHL => Some((c, BinOp::LeftShift)), | ||
621 | SHR => Some((c, BinOp::RightShift)), | ||
622 | CARET => Some((c, BinOp::BitwiseXor)), | ||
623 | PIPE => Some((c, BinOp::BitwiseOr)), | ||
624 | AMP => Some((c, BinOp::BitwiseAnd)), | ||
625 | DOTDOT => Some((c, BinOp::RangeRightOpen)), | ||
626 | DOTDOTEQ => Some((c, BinOp::RangeRightClosed)), | ||
627 | EQ => Some((c, BinOp::Assignment)), | ||
628 | PLUSEQ => Some((c, BinOp::AddAssign)), | ||
629 | SLASHEQ => Some((c, BinOp::DivAssign)), | ||
630 | STAREQ => Some((c, BinOp::MulAssign)), | ||
631 | PERCENTEQ => Some((c, BinOp::RemAssign)), | ||
632 | SHREQ => Some((c, BinOp::ShrAssign)), | ||
633 | SHLEQ => Some((c, BinOp::ShlAssign)), | ||
634 | MINUSEQ => Some((c, BinOp::SubAssign)), | ||
635 | PIPEEQ => Some((c, BinOp::BitOrAssign)), | ||
636 | AMPEQ => Some((c, BinOp::BitAndAssign)), | ||
637 | CARETEQ => Some((c, BinOp::BitXorAssign)), | ||
638 | _ => None, | ||
639 | }) | ||
640 | } | ||
641 | |||
642 | pub fn op_kind(&self) -> Option<BinOp> { | ||
643 | self.op_details().map(|t| t.1) | ||
644 | } | ||
645 | |||
646 | pub fn op(&self) -> Option<&SyntaxNode> { | ||
647 | self.op_details().map(|t| t.0) | ||
648 | } | ||
649 | |||
650 | pub fn lhs(&self) -> Option<&Expr> { | ||
651 | children(self).nth(0) | ||
652 | } | ||
653 | |||
654 | pub fn rhs(&self) -> Option<&Expr> { | ||
655 | children(self).nth(1) | ||
656 | } | ||
657 | |||
658 | pub fn sub_exprs(&self) -> (Option<&Expr>, Option<&Expr>) { | ||
659 | let mut children = children(self); | ||
660 | let first = children.next(); | ||
661 | let second = children.next(); | ||
662 | (first, second) | ||
663 | } | ||
664 | } | ||
665 | |||
666 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
667 | pub enum SelfParamFlavor { | ||
668 | /// self | ||
669 | Owned, | ||
670 | /// &self | ||
671 | Ref, | ||
672 | /// &mut self | ||
673 | MutRef, | ||
674 | } | ||
675 | |||
676 | impl SelfParam { | ||
677 | pub fn flavor(&self) -> SelfParamFlavor { | ||
678 | let borrowed = self.syntax().children().any(|n| n.kind() == AMP); | ||
679 | if borrowed { | ||
680 | // check for a `mut` coming after the & -- `mut &self` != `&mut self` | ||
681 | if self.syntax().children().skip_while(|n| n.kind() != AMP).any(|n| n.kind() == MUT_KW) | ||
682 | { | ||
683 | SelfParamFlavor::MutRef | ||
684 | } else { | ||
685 | SelfParamFlavor::Ref | ||
686 | } | ||
687 | } else { | ||
688 | SelfParamFlavor::Owned | ||
689 | } | ||
690 | } | ||
691 | } | ||
692 | |||
693 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
694 | pub enum LiteralFlavor { | ||
695 | String, | ||
696 | ByteString, | ||
697 | Char, | ||
698 | Byte, | ||
699 | IntNumber { suffix: Option<SmolStr> }, | ||
700 | FloatNumber { suffix: Option<SmolStr> }, | ||
701 | Bool, | ||
702 | } | ||
703 | |||
704 | impl LiteralExpr { | ||
705 | pub fn flavor(&self) -> LiteralFlavor { | ||
706 | let syntax = self.syntax(); | ||
707 | match syntax.kind() { | ||
708 | INT_NUMBER => { | ||
709 | let allowed_suffix_list = [ | ||
710 | "isize", "i128", "i64", "i32", "i16", "i8", "usize", "u128", "u64", "u32", | ||
711 | "u16", "u8", | ||
712 | ]; | ||
713 | let text = syntax.text().to_string(); | ||
714 | let suffix = allowed_suffix_list | ||
715 | .iter() | ||
716 | .find(|&s| text.ends_with(s)) | ||
717 | .map(|&suf| SmolStr::new(suf)); | ||
718 | LiteralFlavor::IntNumber { suffix: suffix } | ||
719 | } | ||
720 | FLOAT_NUMBER => { | ||
721 | let allowed_suffix_list = ["f64", "f32"]; | ||
722 | let text = syntax.text().to_string(); | ||
723 | let suffix = allowed_suffix_list | ||
724 | .iter() | ||
725 | .find(|&s| text.ends_with(s)) | ||
726 | .map(|&suf| SmolStr::new(suf)); | ||
727 | LiteralFlavor::FloatNumber { suffix: suffix } | ||
728 | } | ||
729 | STRING | RAW_STRING => LiteralFlavor::String, | ||
730 | TRUE_KW | FALSE_KW => LiteralFlavor::Bool, | ||
731 | BYTE_STRING | RAW_BYTE_STRING => LiteralFlavor::ByteString, | ||
732 | CHAR => LiteralFlavor::Char, | ||
733 | BYTE => LiteralFlavor::Byte, | ||
734 | _ => unreachable!(), | ||
735 | } | ||
736 | } | ||
737 | } | ||
738 | |||
739 | impl NamedField { | ||
740 | pub fn parent_struct_lit(&self) -> &StructLit { | ||
741 | self.syntax().ancestors().find_map(StructLit::cast).unwrap() | ||
742 | } | ||
743 | } | 70 | } |
744 | 71 | ||
745 | impl BindPat { | 72 | fn children<P: AstNode, C: AstNode>(parent: &P) -> AstChildren<C> { |
746 | pub fn is_mutable(&self) -> bool { | 73 | AstChildren::new(parent.syntax()) |
747 | self.syntax().children().any(|n| n.kind() == MUT_KW) | ||
748 | } | ||
749 | |||
750 | pub fn is_ref(&self) -> bool { | ||
751 | self.syntax().children().any(|n| n.kind() == REF_KW) | ||
752 | } | ||
753 | } | 74 | } |
754 | 75 | ||
755 | #[test] | 76 | #[test] |
@@ -793,3 +114,70 @@ fn test_doc_comment_preserves_indents() { | |||
793 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 114 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
794 | assert_eq!("doc1\n```\nfn foo() {\n // ...\n}\n```", module.doc_comment_text().unwrap()); | 115 | assert_eq!("doc1\n```\nfn foo() {\n // ...\n}\n```", module.doc_comment_text().unwrap()); |
795 | } | 116 | } |
117 | |||
118 | #[test] | ||
119 | fn test_where_predicates() { | ||
120 | fn assert_bound(text: &str, bound: Option<&TypeBound>) { | ||
121 | assert_eq!(text, bound.unwrap().syntax().text().to_string()); | ||
122 | } | ||
123 | |||
124 | let file = SourceFile::parse( | ||
125 | r#" | ||
126 | fn foo() | ||
127 | where | ||
128 | T: Clone + Copy + Debug + 'static, | ||
129 | 'a: 'b + 'c, | ||
130 | Iterator::Item: 'a + Debug, | ||
131 | Iterator::Item: Debug + 'a, | ||
132 | <T as Iterator>::Item: Debug + 'a, | ||
133 | for<'a> F: Fn(&'a str) | ||
134 | {} | ||
135 | "#, | ||
136 | ); | ||
137 | let where_clause = file.syntax().descendants().find_map(WhereClause::cast).unwrap(); | ||
138 | |||
139 | let mut predicates = where_clause.predicates(); | ||
140 | |||
141 | let pred = predicates.next().unwrap(); | ||
142 | let mut bounds = pred.type_bound_list().unwrap().bounds(); | ||
143 | |||
144 | assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string()); | ||
145 | assert_bound("Clone", bounds.next()); | ||
146 | assert_bound("Copy", bounds.next()); | ||
147 | assert_bound("Debug", bounds.next()); | ||
148 | assert_bound("'static", bounds.next()); | ||
149 | |||
150 | let pred = predicates.next().unwrap(); | ||
151 | let mut bounds = pred.type_bound_list().unwrap().bounds(); | ||
152 | |||
153 | assert_eq!("'a", pred.lifetime_token().unwrap().text()); | ||
154 | |||
155 | assert_bound("'b", bounds.next()); | ||
156 | assert_bound("'c", bounds.next()); | ||
157 | |||
158 | let pred = predicates.next().unwrap(); | ||
159 | let mut bounds = pred.type_bound_list().unwrap().bounds(); | ||
160 | |||
161 | assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string()); | ||
162 | assert_bound("'a", bounds.next()); | ||
163 | |||
164 | let pred = predicates.next().unwrap(); | ||
165 | let mut bounds = pred.type_bound_list().unwrap().bounds(); | ||
166 | |||
167 | assert_eq!("Iterator::Item", pred.type_ref().unwrap().syntax().text().to_string()); | ||
168 | assert_bound("Debug", bounds.next()); | ||
169 | assert_bound("'a", bounds.next()); | ||
170 | |||
171 | let pred = predicates.next().unwrap(); | ||
172 | let mut bounds = pred.type_bound_list().unwrap().bounds(); | ||
173 | |||
174 | assert_eq!("<T as Iterator>::Item", pred.type_ref().unwrap().syntax().text().to_string()); | ||
175 | assert_bound("Debug", bounds.next()); | ||
176 | assert_bound("'a", bounds.next()); | ||
177 | |||
178 | let pred = predicates.next().unwrap(); | ||
179 | let mut bounds = pred.type_bound_list().unwrap().bounds(); | ||
180 | |||
181 | assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string()); | ||
182 | assert_bound("Fn(&'a str)", bounds.next()); | ||
183 | } | ||