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.rs802
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
2mod generated; 3mod generated;
4mod traits;
5mod tokens;
6mod extensions;
7mod expr_extensions;
3 8
4use std::marker::PhantomData; 9use std::marker::PhantomData;
5 10
6use itertools::Itertools;
7
8pub use self::generated::*;
9use crate::{ 11use crate::{
10 syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes}, 12 syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken},
11 SmolStr, 13 SmolStr,
12 SyntaxKind::*, 14};
15
16pub 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
28pub trait AstToken: AstNode { 37/// Like `AstNode`, but wraps tokens rather than interior nodes.
29 fn text(&self) -> &SmolStr { 38pub 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>;
34pub 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
40pub trait NameOwner: AstNode {
41 fn name(&self) -> Option<&Name> {
42 child_opt(self)
43 }
44}
45
46pub trait VisibilityOwner: AstNode {
47 fn visibility(&self) -> Option<&Visibility> {
48 child_opt(self)
49 }
50}
51
52pub trait LoopBodyOwner: AstNode {
53 fn loop_body(&self) -> Option<&Block> {
54 child_opt(self)
55 }
56}
57
58pub trait ArgListOwner: AstNode {
59 fn arg_list(&self) -> Option<&ArgList> {
60 child_opt(self)
61 }
62}
63
64pub trait FnDefOwner: AstNode {
65 fn functions(&self) -> AstChildren<FnDef> {
66 children(self)
67 }
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum ItemOrMacro<'a> {
72 Item(&'a ModuleItem),
73 Macro(&'a MacroCall),
74}
75
76pub 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)]
86pub struct ItemOrMacroIter<'a>(SyntaxNodeChildren<'a>);
87
88impl<'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
103pub 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
113pub 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
122pub 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
159impl 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
206impl 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)]
238pub enum CommentFlavor {
239 Line,
240 Doc,
241 ModuleDoc,
242 Multiline,
243}
244
245impl 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
264impl 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
274impl Name {
275 pub fn text(&self) -> &SmolStr {
276 let ident = self.syntax().first_child().unwrap();
277 ident.leaf_text().unwrap()
278 }
279}
280
281impl NameRef {
282 pub fn text(&self) -> &SmolStr {
283 let ident = self.syntax().first_child().unwrap();
284 ident.leaf_text().unwrap()
285 }
286}
287
288impl 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
311impl 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
320impl 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)]
330pub enum ElseBranchFlavor<'a> {
331 Block(&'a Block),
332 IfExpr(&'a IfExpr),
333}
334
335impl 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
355impl 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)]
365pub enum PathSegmentKind<'a> {
366 Name(&'a NameRef),
367 SelfKw,
368 SuperKw,
369 CrateKw,
370}
371
372impl 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
399impl Path {
400 pub fn parent_path(&self) -> Option<&Path> {
401 self.syntax().parent().and_then(Path::cast)
402 }
403}
404
405impl UseTree {
406 pub fn has_star(&self) -> bool {
407 self.syntax().children().any(|it| it.kind() == STAR)
408 }
409}
410
411impl 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
420impl RefPat {
421 pub fn is_mut(&self) -> bool {
422 self.syntax().children().any(|n| n.kind() == MUT_KW)
423 } 45 }
424} 46}
425 47
426fn 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
430fn children<P: AstNode, C: AstNode>(parent: &P) -> AstChildren<C> {
431 AstChildren::new(parent.syntax())
432}
433
434#[derive(Debug)] 49#[derive(Debug)]
435pub struct AstChildren<'a, N> { 50pub struct AstChildren<'a, N> {
436 inner: SyntaxNodeChildren<'a>, 51 inner: SyntaxNodeChildren<'a>,
@@ -446,310 +61,16 @@ impl<'a, N> AstChildren<'a, N> {
446impl<'a, N: AstNode + 'a> Iterator for AstChildren<'a, N> { 61impl<'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)]
458pub enum StructFlavor<'a> {
459 Tuple(&'a PosFieldDefList),
460 Named(&'a NamedFieldDefList),
461 Unit,
462}
463
464impl 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
476impl StructDef { 68fn 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
482impl 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
495impl PointerType {
496 pub fn is_mut(&self) -> bool {
497 self.syntax().children().any(|n| n.kind() == MUT_KW)
498 }
499}
500
501impl ReferenceType {
502 pub fn is_mut(&self) -> bool {
503 self.syntax().children().any(|n| n.kind() == MUT_KW)
504 }
505}
506
507impl 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)]
514pub 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
523impl 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)]
539pub 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
604impl 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)]
667pub enum SelfParamFlavor {
668 /// self
669 Owned,
670 /// &self
671 Ref,
672 /// &mut self
673 MutRef,
674}
675
676impl 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)]
694pub enum LiteralFlavor {
695 String,
696 ByteString,
697 Char,
698 Byte,
699 IntNumber { suffix: Option<SmolStr> },
700 FloatNumber { suffix: Option<SmolStr> },
701 Bool,
702}
703
704impl 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
739impl NamedField {
740 pub fn parent_struct_lit(&self) -> &StructLit {
741 self.syntax().ancestors().find_map(StructLit::cast).unwrap()
742 }
743} 70}
744 71
745impl BindPat { 72fn 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]
119fn 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#"
126fn foo()
127where
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}