aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/add_missing_impl_members.rs2
-rw-r--r--crates/ra_assists/src/inline_local_variable.rs9
-rw-r--r--crates/ra_assists/src/replace_if_let_with_match.rs4
-rw-r--r--crates/ra_fmt/src/lib.rs3
-rw-r--r--crates/ra_hir/src/adt.rs28
-rw-r--r--crates/ra_hir/src/code_model_impl/function.rs8
-rw-r--r--crates/ra_hir/src/expr.rs26
-rw-r--r--crates/ra_ide_api/src/completion/snapshots/completion_item__enum_variant_with_details.snap9
-rw-r--r--crates/ra_ide_api/src/extend_selection.rs12
-rw-r--r--crates/ra_ide_api/src/folding_ranges.rs17
-rw-r--r--crates/ra_ide_api/src/join_lines.rs4
-rw-r--r--crates/ra_ide_api/src/typing.rs4
-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
17 files changed, 913 insertions, 845 deletions
diff --git a/crates/ra_assists/src/add_missing_impl_members.rs b/crates/ra_assists/src/add_missing_impl_members.rs
index 5b01e898e..19a2d05bc 100644
--- a/crates/ra_assists/src/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/add_missing_impl_members.rs
@@ -5,7 +5,7 @@ use crate::{Assist, AssistId, AssistCtx};
5use hir::Resolver; 5use hir::Resolver;
6use hir::db::HirDatabase; 6use hir::db::HirDatabase;
7use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; 7use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc};
8use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner}; 8use ra_syntax::ast::{self, AstNode, AstToken, FnDef, ImplItem, ImplItemKind, NameOwner};
9use ra_db::FilePosition; 9use ra_db::FilePosition;
10use ra_fmt::{leading_indent, reindent}; 10use ra_fmt::{leading_indent, reindent};
11 11
diff --git a/crates/ra_assists/src/inline_local_variable.rs b/crates/ra_assists/src/inline_local_variable.rs
index 0d7b884b8..950c2910b 100644
--- a/crates/ra_assists/src/inline_local_variable.rs
+++ b/crates/ra_assists/src/inline_local_variable.rs
@@ -1,14 +1,9 @@
1use hir::{ 1use hir::{
2 db::HirDatabase, 2 db::HirDatabase,
3 source_binder::function_from_child_node 3 source_binder::function_from_child_node,
4}; 4};
5use ra_syntax::{ 5use ra_syntax::{
6 ast::{ 6 ast::{self, AstNode, AstToken, PatKind, ExprKind},
7 self,
8 AstNode,
9 PatKind,
10 ExprKind
11 },
12 TextRange, 7 TextRange,
13}; 8};
14 9
diff --git a/crates/ra_assists/src/replace_if_let_with_match.rs b/crates/ra_assists/src/replace_if_let_with_match.rs
index 230573499..2b451f08d 100644
--- a/crates/ra_assists/src/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/replace_if_let_with_match.rs
@@ -11,8 +11,8 @@ pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) ->
11 let expr = cond.expr()?; 11 let expr = cond.expr()?;
12 let then_block = if_expr.then_branch()?; 12 let then_block = if_expr.then_branch()?;
13 let else_block = match if_expr.else_branch()? { 13 let else_block = match if_expr.else_branch()? {
14 ast::ElseBranchFlavor::Block(it) => it, 14 ast::ElseBranch::Block(it) => it,
15 ast::ElseBranchFlavor::IfExpr(_) => return None, 15 ast::ElseBranch::IfExpr(_) => return None,
16 }; 16 };
17 17
18 ctx.add_action(AssistId("replace_if_let_with_match"), "replace with match", |edit| { 18 ctx.add_action(AssistId("replace_if_let_with_match"), "replace with match", |edit| {
diff --git a/crates/ra_fmt/src/lib.rs b/crates/ra_fmt/src/lib.rs
index ea90dc2b8..85b7ce250 100644
--- a/crates/ra_fmt/src/lib.rs
+++ b/crates/ra_fmt/src/lib.rs
@@ -2,9 +2,8 @@
2//! 2//!
3use itertools::Itertools; 3use itertools::Itertools;
4use ra_syntax::{ 4use ra_syntax::{
5 AstNode,
6 SyntaxNode, SyntaxKind::*, SyntaxToken, SyntaxKind, 5 SyntaxNode, SyntaxKind::*, SyntaxToken, SyntaxKind,
7 ast, 6 ast::{self, AstNode, AstToken},
8 algo::generate, 7 algo::generate,
9}; 8};
10 9
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs
index 78ea8976b..e027eedd9 100644
--- a/crates/ra_hir/src/adt.rs
+++ b/crates/ra_hir/src/adt.rs
@@ -6,7 +6,7 @@ use std::sync::Arc;
6use ra_arena::{RawId, Arena, impl_arena_id}; 6use ra_arena::{RawId, Arena, impl_arena_id};
7use ra_syntax::{ 7use ra_syntax::{
8 TreeArc, 8 TreeArc,
9 ast::{self, NameOwner, StructFlavor, TypeAscriptionOwner} 9 ast::{self, NameOwner, StructKind, TypeAscriptionOwner}
10}; 10};
11 11
12use crate::{ 12use crate::{
@@ -47,7 +47,7 @@ pub struct StructData {
47impl StructData { 47impl StructData {
48 fn new(struct_def: &ast::StructDef) -> StructData { 48 fn new(struct_def: &ast::StructDef) -> StructData {
49 let name = struct_def.name().map(|n| n.as_name()); 49 let name = struct_def.name().map(|n| n.as_name());
50 let variant_data = VariantData::new(struct_def.flavor()); 50 let variant_data = VariantData::new(struct_def.kind());
51 let variant_data = Arc::new(variant_data); 51 let variant_data = Arc::new(variant_data);
52 StructData { name, variant_data } 52 StructData { name, variant_data }
53 } 53 }
@@ -94,7 +94,7 @@ impl EnumData {
94 let variants = variants(&*enum_def) 94 let variants = variants(&*enum_def)
95 .map(|var| EnumVariantData { 95 .map(|var| EnumVariantData {
96 name: var.name().map(|it| it.as_name()), 96 name: var.name().map(|it| it.as_name()),
97 variant_data: Arc::new(VariantData::new(var.flavor())), 97 variant_data: Arc::new(VariantData::new(var.kind())),
98 }) 98 })
99 .collect(); 99 .collect();
100 Arc::new(EnumData { name, variants }) 100 Arc::new(EnumData { name, variants })
@@ -143,9 +143,9 @@ impl VariantData {
143} 143}
144 144
145impl VariantData { 145impl VariantData {
146 fn new(flavor: StructFlavor) -> Self { 146 fn new(flavor: StructKind) -> Self {
147 let inner = match flavor { 147 let inner = match flavor {
148 ast::StructFlavor::Tuple(fl) => { 148 ast::StructKind::Tuple(fl) => {
149 let fields = fl 149 let fields = fl
150 .fields() 150 .fields()
151 .enumerate() 151 .enumerate()
@@ -156,7 +156,7 @@ impl VariantData {
156 .collect(); 156 .collect();
157 VariantDataInner::Tuple(fields) 157 VariantDataInner::Tuple(fields)
158 } 158 }
159 ast::StructFlavor::Named(fl) => { 159 ast::StructKind::Named(fl) => {
160 let fields = fl 160 let fields = fl
161 .fields() 161 .fields()
162 .map(|fd| StructFieldData { 162 .map(|fd| StructFieldData {
@@ -166,7 +166,7 @@ impl VariantData {
166 .collect(); 166 .collect();
167 VariantDataInner::Struct(fields) 167 VariantDataInner::Struct(fields)
168 } 168 }
169 ast::StructFlavor::Unit => VariantDataInner::Unit, 169 ast::StructKind::Unit => VariantDataInner::Unit,
170 }; 170 };
171 VariantData(inner) 171 VariantData(inner)
172 } 172 }
@@ -200,27 +200,27 @@ impl StructField {
200 let fields = var_data.fields().unwrap(); 200 let fields = var_data.fields().unwrap();
201 let ss; 201 let ss;
202 let es; 202 let es;
203 let (file_id, struct_flavor) = match self.parent { 203 let (file_id, struct_kind) = match self.parent {
204 VariantDef::Struct(s) => { 204 VariantDef::Struct(s) => {
205 let (file_id, source) = s.source(db); 205 let (file_id, source) = s.source(db);
206 ss = source; 206 ss = source;
207 (file_id, ss.flavor()) 207 (file_id, ss.kind())
208 } 208 }
209 VariantDef::EnumVariant(e) => { 209 VariantDef::EnumVariant(e) => {
210 let (file_id, source) = e.source(db); 210 let (file_id, source) = e.source(db);
211 es = source; 211 es = source;
212 (file_id, es.flavor()) 212 (file_id, es.kind())
213 } 213 }
214 }; 214 };
215 215
216 let field_sources = match struct_flavor { 216 let field_sources = match struct_kind {
217 ast::StructFlavor::Tuple(fl) => { 217 ast::StructKind::Tuple(fl) => {
218 fl.fields().map(|it| FieldSource::Pos(it.to_owned())).collect() 218 fl.fields().map(|it| FieldSource::Pos(it.to_owned())).collect()
219 } 219 }
220 ast::StructFlavor::Named(fl) => { 220 ast::StructKind::Named(fl) => {
221 fl.fields().map(|it| FieldSource::Named(it.to_owned())).collect() 221 fl.fields().map(|it| FieldSource::Named(it.to_owned())).collect()
222 } 222 }
223 ast::StructFlavor::Unit => Vec::new(), 223 ast::StructKind::Unit => Vec::new(),
224 }; 224 };
225 let field = field_sources 225 let field = field_sources
226 .into_iter() 226 .into_iter()
diff --git a/crates/ra_hir/src/code_model_impl/function.rs b/crates/ra_hir/src/code_model_impl/function.rs
index 334cb302b..f8bd0f784 100644
--- a/crates/ra_hir/src/code_model_impl/function.rs
+++ b/crates/ra_hir/src/code_model_impl/function.rs
@@ -20,12 +20,12 @@ impl FnSignature {
20 TypeRef::from_ast(type_ref) 20 TypeRef::from_ast(type_ref)
21 } else { 21 } else {
22 let self_type = TypeRef::Path(Name::self_type().into()); 22 let self_type = TypeRef::Path(Name::self_type().into());
23 match self_param.flavor() { 23 match self_param.kind() {
24 ast::SelfParamFlavor::Owned => self_type, 24 ast::SelfParamKind::Owned => self_type,
25 ast::SelfParamFlavor::Ref => { 25 ast::SelfParamKind::Ref => {
26 TypeRef::Reference(Box::new(self_type), Mutability::Shared) 26 TypeRef::Reference(Box::new(self_type), Mutability::Shared)
27 } 27 }
28 ast::SelfParamFlavor::MutRef => { 28 ast::SelfParamKind::MutRef => {
29 TypeRef::Reference(Box::new(self_type), Mutability::Mut) 29 TypeRef::Reference(Box::new(self_type), Mutability::Mut)
30 } 30 }
31 } 31 }
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 946c9faf2..45012827f 100644
--- a/crates/ra_hir/src/expr.rs
+++ b/crates/ra_hir/src/expr.rs
@@ -6,7 +6,7 @@ use rustc_hash::FxHashMap;
6use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; 6use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
7use ra_syntax::{ 7use ra_syntax::{
8 SyntaxNodePtr, AstPtr, AstNode, 8 SyntaxNodePtr, AstPtr, AstNode,
9 ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralFlavor, TypeAscriptionOwner} 9 ast::{self, LoopBodyOwner, ArgListOwner, NameOwner, LiteralKind, TypeAscriptionOwner}
10}; 10};
11 11
12use crate::{ 12use crate::{
@@ -516,8 +516,8 @@ impl ExprCollector {
516 let else_branch = e 516 let else_branch = e
517 .else_branch() 517 .else_branch()
518 .map(|b| match b { 518 .map(|b| match b {
519 ast::ElseBranchFlavor::Block(it) => self.collect_block(it), 519 ast::ElseBranch::Block(it) => self.collect_block(it),
520 ast::ElseBranchFlavor::IfExpr(elif) => { 520 ast::ElseBranch::IfExpr(elif) => {
521 let expr: &ast::Expr = ast::Expr::cast(elif.syntax()).unwrap(); 521 let expr: &ast::Expr = ast::Expr::cast(elif.syntax()).unwrap();
522 self.collect_expr(expr) 522 self.collect_expr(expr)
523 } 523 }
@@ -533,8 +533,8 @@ impl ExprCollector {
533 let condition = self.collect_expr_opt(e.condition().and_then(|c| c.expr())); 533 let condition = self.collect_expr_opt(e.condition().and_then(|c| c.expr()));
534 let then_branch = self.collect_block_opt(e.then_branch()); 534 let then_branch = self.collect_block_opt(e.then_branch());
535 let else_branch = e.else_branch().map(|b| match b { 535 let else_branch = e.else_branch().map(|b| match b {
536 ast::ElseBranchFlavor::Block(it) => self.collect_block(it), 536 ast::ElseBranch::Block(it) => self.collect_block(it),
537 ast::ElseBranchFlavor::IfExpr(elif) => { 537 ast::ElseBranch::IfExpr(elif) => {
538 let expr: &ast::Expr = ast::Expr::cast(elif.syntax()).unwrap(); 538 let expr: &ast::Expr = ast::Expr::cast(elif.syntax()).unwrap();
539 self.collect_expr(expr) 539 self.collect_expr(expr)
540 } 540 }
@@ -726,8 +726,8 @@ impl ExprCollector {
726 self.alloc_expr(Expr::Array { exprs }, syntax_ptr) 726 self.alloc_expr(Expr::Array { exprs }, syntax_ptr)
727 } 727 }
728 ast::ExprKind::Literal(e) => { 728 ast::ExprKind::Literal(e) => {
729 let lit = match e.flavor() { 729 let lit = match e.kind() {
730 LiteralFlavor::IntNumber { suffix } => { 730 LiteralKind::IntNumber { suffix } => {
731 let known_name = suffix 731 let known_name = suffix
732 .and_then(|it| IntTy::from_suffix(&it).map(UncertainIntTy::Known)); 732 .and_then(|it| IntTy::from_suffix(&it).map(UncertainIntTy::Known));
733 733
@@ -736,7 +736,7 @@ impl ExprCollector {
736 known_name.unwrap_or(UncertainIntTy::Unknown), 736 known_name.unwrap_or(UncertainIntTy::Unknown),
737 ) 737 )
738 } 738 }
739 LiteralFlavor::FloatNumber { suffix } => { 739 LiteralKind::FloatNumber { suffix } => {
740 let known_name = suffix 740 let known_name = suffix
741 .and_then(|it| FloatTy::from_suffix(&it).map(UncertainFloatTy::Known)); 741 .and_then(|it| FloatTy::from_suffix(&it).map(UncertainFloatTy::Known));
742 742
@@ -745,13 +745,13 @@ impl ExprCollector {
745 known_name.unwrap_or(UncertainFloatTy::Unknown), 745 known_name.unwrap_or(UncertainFloatTy::Unknown),
746 ) 746 )
747 } 747 }
748 LiteralFlavor::ByteString => Literal::ByteString(Default::default()), 748 LiteralKind::ByteString => Literal::ByteString(Default::default()),
749 LiteralFlavor::String => Literal::String(Default::default()), 749 LiteralKind::String => Literal::String(Default::default()),
750 LiteralFlavor::Byte => { 750 LiteralKind::Byte => {
751 Literal::Int(Default::default(), UncertainIntTy::Known(IntTy::u8())) 751 Literal::Int(Default::default(), UncertainIntTy::Known(IntTy::u8()))
752 } 752 }
753 LiteralFlavor::Bool => Literal::Bool(Default::default()), 753 LiteralKind::Bool => Literal::Bool(Default::default()),
754 LiteralFlavor::Char => Literal::Char(Default::default()), 754 LiteralKind::Char => Literal::Char(Default::default()),
755 }; 755 };
756 self.alloc_expr(Expr::Literal(lit), syntax_ptr) 756 self.alloc_expr(Expr::Literal(lit), syntax_ptr)
757 } 757 }
diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__enum_variant_with_details.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__enum_variant_with_details.snap
index 70ea96e1b..daccd9fba 100644
--- a/crates/ra_ide_api/src/completion/snapshots/completion_item__enum_variant_with_details.snap
+++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__enum_variant_with_details.snap
@@ -1,6 +1,6 @@
1--- 1---
2created: "2019-02-18T09:22:24.062138085Z" 2created: "2019-04-02T07:43:12.954637543Z"
3creator: insta@0.6.2 3creator: insta@0.7.4
4source: crates/ra_ide_api/src/completion/completion_item.rs 4source: crates/ra_ide_api/src/completion/completion_item.rs
5expression: kind_completions 5expression: kind_completions
6--- 6---
@@ -33,6 +33,9 @@ expression: kind_completions
33 delete: [180; 180), 33 delete: [180; 180),
34 insert: "S", 34 insert: "S",
35 kind: EnumVariant, 35 kind: EnumVariant,
36 detail: "(S)" 36 detail: "(S)",
37 documentation: Documentation(
38 ""
39 )
37 } 40 }
38] 41]
diff --git a/crates/ra_ide_api/src/extend_selection.rs b/crates/ra_ide_api/src/extend_selection.rs
index e743bf0fe..7293ba359 100644
--- a/crates/ra_ide_api/src/extend_selection.rs
+++ b/crates/ra_ide_api/src/extend_selection.rs
@@ -1,9 +1,9 @@
1use ra_db::SourceDatabase; 1use ra_db::SourceDatabase;
2use ra_syntax::{ 2use ra_syntax::{
3 Direction, SyntaxNode, TextRange, TextUnit, AstNode, SyntaxElement, 3 Direction, SyntaxNode, TextRange, TextUnit, SyntaxElement,
4 algo::{find_covering_element, find_token_at_offset, TokenAtOffset}, 4 algo::{find_covering_element, find_token_at_offset, TokenAtOffset},
5 SyntaxKind::*, SyntaxToken, 5 SyntaxKind::*, SyntaxToken,
6 ast::Comment, 6 ast::{self, AstNode, AstToken},
7}; 7};
8 8
9use crate::{FileRange, db::RootDatabase}; 9use crate::{FileRange, db::RootDatabase};
@@ -55,7 +55,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
55 if token.range() != range { 55 if token.range() != range {
56 return Some(token.range()); 56 return Some(token.range());
57 } 57 }
58 if let Some(comment) = Comment::cast(token) { 58 if let Some(comment) = ast::Comment::cast(token) {
59 if let Some(range) = extend_comments(comment) { 59 if let Some(range) = extend_comments(comment) {
60 return Some(range); 60 return Some(range);
61 } 61 }
@@ -176,7 +176,7 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> {
176 None 176 None
177} 177}
178 178
179fn extend_comments(comment: Comment) -> Option<TextRange> { 179fn extend_comments(comment: ast::Comment) -> Option<TextRange> {
180 let prev = adj_comments(comment, Direction::Prev); 180 let prev = adj_comments(comment, Direction::Prev);
181 let next = adj_comments(comment, Direction::Next); 181 let next = adj_comments(comment, Direction::Next);
182 if prev != next { 182 if prev != next {
@@ -186,14 +186,14 @@ fn extend_comments(comment: Comment) -> Option<TextRange> {
186 } 186 }
187} 187}
188 188
189fn adj_comments(comment: Comment, dir: Direction) -> Comment { 189fn adj_comments(comment: ast::Comment, dir: Direction) -> ast::Comment {
190 let mut res = comment; 190 let mut res = comment;
191 for element in comment.syntax().siblings_with_tokens(dir) { 191 for element in comment.syntax().siblings_with_tokens(dir) {
192 let token = match element.as_token() { 192 let token = match element.as_token() {
193 None => break, 193 None => break,
194 Some(token) => token, 194 Some(token) => token,
195 }; 195 };
196 if let Some(c) = Comment::cast(token) { 196 if let Some(c) = ast::Comment::cast(token) {
197 res = c 197 res = c
198 } else if token.kind() != WHITESPACE || token.text().contains("\n\n") { 198 } else if token.kind() != WHITESPACE || token.text().contains("\n\n") {
199 break; 199 break;
diff --git a/crates/ra_ide_api/src/folding_ranges.rs b/crates/ra_ide_api/src/folding_ranges.rs
index a6fe8a5d5..6987fcc9e 100644
--- a/crates/ra_ide_api/src/folding_ranges.rs
+++ b/crates/ra_ide_api/src/folding_ranges.rs
@@ -1,9 +1,9 @@
1use rustc_hash::FxHashSet; 1use rustc_hash::FxHashSet;
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 AstNode, SourceFile, SyntaxNode, TextRange, Direction, SyntaxElement, 4 SourceFile, SyntaxNode, TextRange, Direction, SyntaxElement,
5 SyntaxKind::{self, *}, 5 SyntaxKind::{self, *},
6 ast::{self, VisibilityOwner, Comment}, 6 ast::{self, AstNode, AstToken, VisibilityOwner},
7}; 7};
8 8
9#[derive(Debug, PartialEq, Eq)] 9#[derive(Debug, PartialEq, Eq)]
@@ -139,13 +139,16 @@ fn contiguous_range_for_group_unless<'a>(
139} 139}
140 140
141fn contiguous_range_for_comment<'a>( 141fn contiguous_range_for_comment<'a>(
142 first: Comment<'a>, 142 first: ast::Comment<'a>,
143 visited: &mut FxHashSet<Comment<'a>>, 143 visited: &mut FxHashSet<ast::Comment<'a>>,
144) -> Option<TextRange> { 144) -> Option<TextRange> {
145 visited.insert(first); 145 visited.insert(first);
146 146
147 // Only fold comments of the same flavor 147 // Only fold comments of the same flavor
148 let group_flavor = first.flavor(); 148 let group_kind = first.kind();
149 if !group_kind.shape.is_line() {
150 return None;
151 }
149 152
150 let mut last = first; 153 let mut last = first;
151 for element in first.syntax().siblings_with_tokens(Direction::Next) { 154 for element in first.syntax().siblings_with_tokens(Direction::Next) {
@@ -157,8 +160,8 @@ fn contiguous_range_for_comment<'a>(
157 continue; 160 continue;
158 } 161 }
159 } 162 }
160 if let Some(c) = Comment::cast(token) { 163 if let Some(c) = ast::Comment::cast(token) {
161 if c.flavor() == group_flavor { 164 if c.kind() == group_kind {
162 visited.insert(c); 165 visited.insert(c);
163 last = c; 166 last = c;
164 continue; 167 continue;
diff --git a/crates/ra_ide_api/src/join_lines.rs b/crates/ra_ide_api/src/join_lines.rs
index 57b6f8384..598717311 100644
--- a/crates/ra_ide_api/src/join_lines.rs
+++ b/crates/ra_ide_api/src/join_lines.rs
@@ -1,9 +1,9 @@
1use itertools::Itertools; 1use itertools::Itertools;
2use ra_syntax::{ 2use ra_syntax::{
3 SourceFile, TextRange, TextUnit, AstNode, SyntaxNode, SyntaxElement, SyntaxToken, 3 SourceFile, TextRange, TextUnit, SyntaxNode, SyntaxElement, SyntaxToken,
4 SyntaxKind::{self, WHITESPACE, COMMA, R_CURLY, R_PAREN, R_BRACK}, 4 SyntaxKind::{self, WHITESPACE, COMMA, R_CURLY, R_PAREN, R_BRACK},
5 algo::{find_covering_element, non_trivia_sibling}, 5 algo::{find_covering_element, non_trivia_sibling},
6 ast, 6 ast::{self, AstNode, AstToken},
7 Direction, 7 Direction,
8}; 8};
9use ra_fmt::{ 9use ra_fmt::{
diff --git a/crates/ra_ide_api/src/typing.rs b/crates/ra_ide_api/src/typing.rs
index 4510d663d..ae53bca77 100644
--- a/crates/ra_ide_api/src/typing.rs
+++ b/crates/ra_ide_api/src/typing.rs
@@ -2,7 +2,7 @@ use ra_syntax::{
2 AstNode, SourceFile, SyntaxKind::*, 2 AstNode, SourceFile, SyntaxKind::*,
3 TextUnit, TextRange, SyntaxToken, 3 TextUnit, TextRange, SyntaxToken,
4 algo::{find_node_at_offset, find_token_at_offset, TokenAtOffset}, 4 algo::{find_node_at_offset, find_token_at_offset, TokenAtOffset},
5 ast::{self}, 5 ast::{self, AstToken},
6}; 6};
7use ra_fmt::leading_indent; 7use ra_fmt::leading_indent;
8use ra_text_edit::{TextEdit, TextEditBuilder}; 8use ra_text_edit::{TextEdit, TextEditBuilder};
@@ -15,7 +15,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour
15 .left_biased() 15 .left_biased()
16 .and_then(ast::Comment::cast)?; 16 .and_then(ast::Comment::cast)?;
17 17
18 if comment.flavor() == ast::CommentFlavor::Multiline { 18 if comment.kind().shape.is_block() {
19 return None; 19 return None;
20 } 20 }
21 21
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}