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