diff options
-rw-r--r-- | crates/ra_assists/src/add_missing_impl_members.rs | 2 | ||||
-rw-r--r-- | crates/ra_assists/src/inline_local_variable.rs | 9 | ||||
-rw-r--r-- | crates/ra_assists/src/replace_if_let_with_match.rs | 4 | ||||
-rw-r--r-- | crates/ra_fmt/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir/src/adt.rs | 28 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model_impl/function.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir/src/expr.rs | 26 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/snapshots/completion_item__enum_variant_with_details.snap | 9 | ||||
-rw-r--r-- | crates/ra_ide_api/src/extend_selection.rs | 12 | ||||
-rw-r--r-- | crates/ra_ide_api/src/folding_ranges.rs | 17 | ||||
-rw-r--r-- | crates/ra_ide_api/src/join_lines.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide_api/src/typing.rs | 4 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast.rs | 810 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/expr_extensions.rs | 252 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/extensions.rs | 303 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/tokens.rs | 113 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/traits.rs | 154 |
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}; | |||
5 | use hir::Resolver; | 5 | use hir::Resolver; |
6 | use hir::db::HirDatabase; | 6 | use hir::db::HirDatabase; |
7 | use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; | 7 | use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc}; |
8 | use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner}; | 8 | use ra_syntax::ast::{self, AstNode, AstToken, FnDef, ImplItem, ImplItemKind, NameOwner}; |
9 | use ra_db::FilePosition; | 9 | use ra_db::FilePosition; |
10 | use ra_fmt::{leading_indent, reindent}; | 10 | use 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 @@ | |||
1 | use hir::{ | 1 | use hir::{ |
2 | db::HirDatabase, | 2 | db::HirDatabase, |
3 | source_binder::function_from_child_node | 3 | source_binder::function_from_child_node, |
4 | }; | 4 | }; |
5 | use ra_syntax::{ | 5 | use 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 | //! |
3 | use itertools::Itertools; | 3 | use itertools::Itertools; |
4 | use ra_syntax::{ | 4 | use 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; | |||
6 | use ra_arena::{RawId, Arena, impl_arena_id}; | 6 | use ra_arena::{RawId, Arena, impl_arena_id}; |
7 | use ra_syntax::{ | 7 | use ra_syntax::{ |
8 | TreeArc, | 8 | TreeArc, |
9 | ast::{self, NameOwner, StructFlavor, TypeAscriptionOwner} | 9 | ast::{self, NameOwner, StructKind, TypeAscriptionOwner} |
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{ |
@@ -47,7 +47,7 @@ pub struct StructData { | |||
47 | impl StructData { | 47 | impl 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 | ||
145 | impl VariantData { | 145 | impl 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; | |||
6 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; | 6 | use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap}; |
7 | use ra_syntax::{ | 7 | use 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 | ||
12 | use crate::{ | 12 | use 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 | --- |
2 | created: "2019-02-18T09:22:24.062138085Z" | 2 | created: "2019-04-02T07:43:12.954637543Z" |
3 | creator: insta@0.6.2 | 3 | creator: insta@0.7.4 |
4 | source: crates/ra_ide_api/src/completion/completion_item.rs | 4 | source: crates/ra_ide_api/src/completion/completion_item.rs |
5 | expression: kind_completions | 5 | expression: 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 @@ | |||
1 | use ra_db::SourceDatabase; | 1 | use ra_db::SourceDatabase; |
2 | use ra_syntax::{ | 2 | use 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 | ||
9 | use crate::{FileRange, db::RootDatabase}; | 9 | use 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 | ||
179 | fn extend_comments(comment: Comment) -> Option<TextRange> { | 179 | fn 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 | ||
189 | fn adj_comments(comment: Comment, dir: Direction) -> Comment { | 189 | fn 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 @@ | |||
1 | use rustc_hash::FxHashSet; | 1 | use rustc_hash::FxHashSet; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use 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 | ||
141 | fn contiguous_range_for_comment<'a>( | 141 | fn 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 @@ | |||
1 | use itertools::Itertools; | 1 | use itertools::Itertools; |
2 | use ra_syntax::{ | 2 | use 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 | }; |
9 | use ra_fmt::{ | 9 | use 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 | }; |
7 | use ra_fmt::leading_indent; | 7 | use ra_fmt::leading_indent; |
8 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 8 | use 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 | |||
2 | mod generated; | 3 | mod generated; |
4 | mod traits; | ||
5 | mod tokens; | ||
6 | mod extensions; | ||
7 | mod expr_extensions; | ||
3 | 8 | ||
4 | use std::marker::PhantomData; | 9 | use std::marker::PhantomData; |
5 | 10 | ||
6 | use itertools::Itertools; | ||
7 | |||
8 | pub use self::generated::*; | ||
9 | use crate::{ | 11 | use crate::{ |
10 | syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken, SyntaxElement, SyntaxElementChildren}, | 12 | syntax_node::{SyntaxNode, SyntaxNodeChildren, TreeArc, RaTypes, SyntaxToken}, |
11 | SmolStr, | 13 | SmolStr, |
12 | SyntaxKind::*, | 14 | }; |
15 | |||
16 | pub use self::{ | ||
17 | generated::*, | ||
18 | traits::*, | ||
19 | tokens::*, | ||
20 | extensions::{PathSegmentKind, StructKind, SelfParamKind}, | ||
21 | expr_extensions::{ElseBranch, PrefixOp, BinOp, LiteralKind}, | ||
13 | }; | 22 | }; |
14 | 23 | ||
15 | /// The main trait to go from untyped `SyntaxNode` to a typed ast. The | 24 | /// The main trait to go from untyped `SyntaxNode` to a typed ast. The |
@@ -25,449 +34,18 @@ pub trait AstNode: | |||
25 | fn syntax(&self) -> &SyntaxNode; | 34 | fn syntax(&self) -> &SyntaxNode; |
26 | } | 35 | } |
27 | 36 | ||
28 | pub trait TypeAscriptionOwner: AstNode { | 37 | /// Like `AstNode`, but wraps tokens rather than interior nodes. |
29 | fn ascribed_type(&self) -> Option<&TypeRef> { | 38 | pub 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>; | |
34 | pub 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 | |||
40 | pub trait VisibilityOwner: AstNode { | ||
41 | fn visibility(&self) -> Option<&Visibility> { | ||
42 | child_opt(self) | ||
43 | } | ||
44 | } | ||
45 | |||
46 | pub trait LoopBodyOwner: AstNode { | ||
47 | fn loop_body(&self) -> Option<&Block> { | ||
48 | child_opt(self) | ||
49 | } | ||
50 | } | ||
51 | |||
52 | pub trait ArgListOwner: AstNode { | ||
53 | fn arg_list(&self) -> Option<&ArgList> { | ||
54 | child_opt(self) | ||
55 | } | ||
56 | } | ||
57 | |||
58 | pub trait FnDefOwner: AstNode { | ||
59 | fn functions(&self) -> AstChildren<FnDef> { | ||
60 | children(self) | ||
61 | } | ||
62 | } | ||
63 | |||
64 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
65 | pub enum ItemOrMacro<'a> { | ||
66 | Item(&'a ModuleItem), | ||
67 | Macro(&'a MacroCall), | ||
68 | } | ||
69 | |||
70 | pub 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)] | ||
80 | pub struct ItemOrMacroIter<'a>(SyntaxNodeChildren<'a>); | ||
81 | |||
82 | impl<'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 | |||
97 | pub 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 | |||
107 | pub trait TypeBoundsOwner: AstNode { | ||
108 | fn type_bound_list(&self) -> Option<&TypeBoundList> { | ||
109 | child_opt(self) | ||
110 | } | ||
111 | } | ||
112 | |||
113 | pub trait AttrsOwner: AstNode { | ||
114 | fn attrs(&self) -> AstChildren<Attr> { | ||
115 | children(self) | ||
116 | } | ||
117 | fn has_atom_attr(&self, atom: &str) -> bool { | ||
118 | self.attrs().filter_map(|x| x.as_atom()).any(|x| x == atom) | ||
119 | } | ||
120 | } | ||
121 | |||
122 | pub trait DocCommentsOwner: AstNode { | ||
123 | fn doc_comments(&self) -> 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 | |||
159 | impl Attr { | ||
160 | pub fn is_inner(&self) -> bool { | ||
161 | let tt = match self.value() { | ||
162 | None => return false, | ||
163 | Some(tt) => tt, | ||
164 | }; | ||
165 | |||
166 | let prev = match tt.syntax().prev_sibling() { | ||
167 | None => return false, | ||
168 | Some(prev) => prev, | ||
169 | }; | ||
170 | |||
171 | prev.kind() == EXCL | ||
172 | } | ||
173 | |||
174 | pub fn as_atom(&self) -> Option<SmolStr> { | ||
175 | let tt = self.value()?; | ||
176 | let (_bra, attr, _ket) = tt.syntax().children_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)] | ||
207 | pub struct Comment<'a>(SyntaxToken<'a>); | ||
208 | |||
209 | impl<'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 | |||
248 | pub struct CommentIter<'a> { | ||
249 | iter: SyntaxElementChildren<'a>, | ||
250 | } | ||
251 | |||
252 | impl<'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)] | ||
260 | pub enum CommentFlavor { | ||
261 | Line, | ||
262 | Doc, | ||
263 | ModuleDoc, | ||
264 | Multiline, | ||
265 | } | ||
266 | |||
267 | impl 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 | |||
286 | pub struct Whitespace<'a>(SyntaxToken<'a>); | ||
287 | |||
288 | impl<'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 | |||
311 | impl 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 | |||
318 | impl 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 | |||
325 | impl 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 | |||
348 | impl 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 | |||
357 | impl 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)] | ||
367 | pub enum ElseBranchFlavor<'a> { | ||
368 | Block(&'a Block), | ||
369 | IfExpr(&'a IfExpr), | ||
370 | } | ||
371 | |||
372 | impl 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 | |||
392 | impl 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)] | ||
402 | pub enum PathSegmentKind<'a> { | ||
403 | Name(&'a NameRef), | ||
404 | SelfKw, | ||
405 | SuperKw, | ||
406 | CrateKw, | ||
407 | } | ||
408 | |||
409 | impl 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 | |||
436 | impl Path { | ||
437 | pub fn parent_path(&self) -> Option<&Path> { | ||
438 | self.syntax().parent().and_then(Path::cast) | ||
439 | } | ||
440 | } | ||
441 | |||
442 | impl UseTree { | ||
443 | pub fn has_star(&self) -> bool { | ||
444 | self.syntax().children_with_tokens().any(|it| it.kind() == STAR) | ||
445 | } | ||
446 | } | ||
447 | |||
448 | impl 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 | |||
457 | impl 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 | ||
463 | fn 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 | |||
467 | fn children<P: AstNode, C: AstNode>(parent: &P) -> AstChildren<C> { | ||
468 | AstChildren::new(parent.syntax()) | ||
469 | } | ||
470 | |||
471 | #[derive(Debug)] | 49 | #[derive(Debug)] |
472 | pub struct AstChildren<'a, N> { | 50 | pub struct AstChildren<'a, N> { |
473 | inner: SyntaxNodeChildren<'a>, | 51 | inner: SyntaxNodeChildren<'a>, |
@@ -483,348 +61,16 @@ impl<'a, N> AstChildren<'a, N> { | |||
483 | impl<'a, N: AstNode + 'a> Iterator for AstChildren<'a, N> { | 61 | impl<'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)] | 68 | fn child_opt<P: AstNode, C: AstNode>(parent: &P) -> Option<&C> { |
495 | pub enum StructFlavor<'a> { | 69 | children(parent).next() |
496 | Tuple(&'a PosFieldDefList), | ||
497 | Named(&'a NamedFieldDefList), | ||
498 | Unit, | ||
499 | } | ||
500 | |||
501 | impl 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 | |||
513 | impl StructDef { | ||
514 | pub fn flavor(&self) -> StructFlavor { | ||
515 | StructFlavor::from_node(self) | ||
516 | } | ||
517 | } | ||
518 | |||
519 | impl 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 | |||
532 | impl PointerType { | ||
533 | pub fn is_mut(&self) -> bool { | ||
534 | self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW) | ||
535 | } | ||
536 | } | ||
537 | |||
538 | impl ReferenceType { | ||
539 | pub fn is_mut(&self) -> bool { | ||
540 | self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW) | ||
541 | } | ||
542 | } | ||
543 | |||
544 | impl 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)] | ||
551 | pub 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 | |||
560 | impl 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)] | ||
576 | pub 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 | |||
641 | impl 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)] | ||
706 | pub enum SelfParamFlavor { | ||
707 | /// self | ||
708 | Owned, | ||
709 | /// &self | ||
710 | Ref, | ||
711 | /// &mut self | ||
712 | MutRef, | ||
713 | } | ||
714 | |||
715 | impl 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)] | ||
745 | pub enum LiteralFlavor { | ||
746 | String, | ||
747 | ByteString, | ||
748 | Char, | ||
749 | Byte, | ||
750 | IntNumber { suffix: Option<SmolStr> }, | ||
751 | FloatNumber { suffix: Option<SmolStr> }, | ||
752 | Bool, | ||
753 | } | ||
754 | |||
755 | impl 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 | |||
796 | impl NamedField { | ||
797 | pub fn parent_struct_lit(&self) -> &StructLit { | ||
798 | self.syntax().ancestors().find_map(StructLit::cast).unwrap() | ||
799 | } | ||
800 | } | ||
801 | |||
802 | impl 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 | |||
812 | impl 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 | ||
821 | impl WherePred { | 72 | fn 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 | |||
3 | use crate::{ | ||
4 | SyntaxToken, SyntaxElement, SmolStr, | ||
5 | ast::{self, AstNode, AstChildren, children, child_opt}, | ||
6 | SyntaxKind::* | ||
7 | }; | ||
8 | |||
9 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
10 | pub enum ElseBranch<'a> { | ||
11 | Block(&'a ast::Block), | ||
12 | IfExpr(&'a ast::IfExpr), | ||
13 | } | ||
14 | |||
15 | impl 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 | |||
35 | impl 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)] | ||
42 | pub 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 | |||
51 | impl 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)] | ||
67 | pub 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 | |||
132 | impl 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)] | ||
197 | pub enum LiteralKind { | ||
198 | String, | ||
199 | ByteString, | ||
200 | Char, | ||
201 | Byte, | ||
202 | IntNumber { suffix: Option<SmolStr> }, | ||
203 | FloatNumber { suffix: Option<SmolStr> }, | ||
204 | Bool, | ||
205 | } | ||
206 | |||
207 | impl 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 | |||
248 | impl 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 | |||
4 | use itertools::Itertools; | ||
5 | |||
6 | use crate::{ | ||
7 | SmolStr, SyntaxToken, | ||
8 | ast::{self, AstNode, children, child_opt}, | ||
9 | SyntaxKind::*, | ||
10 | }; | ||
11 | |||
12 | impl 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 | |||
19 | impl 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 | |||
26 | impl 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)] | ||
74 | pub enum PathSegmentKind<'a> { | ||
75 | Name(&'a ast::NameRef), | ||
76 | SelfKw, | ||
77 | SuperKw, | ||
78 | CrateKw, | ||
79 | } | ||
80 | |||
81 | impl 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 | |||
111 | impl ast::Path { | ||
112 | pub fn parent_path(&self) -> Option<&ast::Path> { | ||
113 | self.syntax().parent().and_then(ast::Path::cast) | ||
114 | } | ||
115 | } | ||
116 | |||
117 | impl 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 | |||
126 | impl ast::UseTree { | ||
127 | pub fn has_star(&self) -> bool { | ||
128 | self.syntax().children_with_tokens().any(|it| it.kind() == STAR) | ||
129 | } | ||
130 | } | ||
131 | |||
132 | impl 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 | |||
141 | impl 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)] | ||
165 | pub enum StructKind<'a> { | ||
166 | Tuple(&'a ast::PosFieldDefList), | ||
167 | Named(&'a ast::NamedFieldDefList), | ||
168 | Unit, | ||
169 | } | ||
170 | |||
171 | impl 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 | |||
183 | impl ast::StructDef { | ||
184 | pub fn kind(&self) -> StructKind { | ||
185 | StructKind::from_node(self) | ||
186 | } | ||
187 | } | ||
188 | |||
189 | impl 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 | |||
202 | impl 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 | |||
211 | impl 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 | |||
220 | impl ast::RefPat { | ||
221 | pub fn is_mut(&self) -> bool { | ||
222 | self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW) | ||
223 | } | ||
224 | } | ||
225 | |||
226 | impl 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 | |||
236 | impl ast::PointerType { | ||
237 | pub fn is_mut(&self) -> bool { | ||
238 | self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW) | ||
239 | } | ||
240 | } | ||
241 | |||
242 | impl 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)] | ||
249 | pub enum SelfParamKind { | ||
250 | /// self | ||
251 | Owned, | ||
252 | /// &self | ||
253 | Ref, | ||
254 | /// &mut self | ||
255 | MutRef, | ||
256 | } | ||
257 | |||
258 | impl 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 | |||
287 | impl 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 | |||
296 | impl 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 | |||
3 | use crate::{ | ||
4 | SyntaxToken, | ||
5 | SyntaxKind::{COMMENT, WHITESPACE}, | ||
6 | ast::AstToken, | ||
7 | }; | ||
8 | |||
9 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
10 | pub struct Comment<'a>(SyntaxToken<'a>); | ||
11 | |||
12 | impl<'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 | |||
25 | impl<'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)] | ||
36 | pub struct CommentKind { | ||
37 | pub shape: CommentShape, | ||
38 | pub doc: Option<CommentPlacement>, | ||
39 | } | ||
40 | |||
41 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
42 | pub enum CommentShape { | ||
43 | Line, | ||
44 | Block, | ||
45 | } | ||
46 | |||
47 | impl 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)] | ||
58 | pub enum CommentPlacement { | ||
59 | Inner, | ||
60 | Outer, | ||
61 | } | ||
62 | |||
63 | const 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 | |||
75 | fn 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 | |||
84 | fn 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 | |||
93 | pub struct Whitespace<'a>(SyntaxToken<'a>); | ||
94 | |||
95 | impl<'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 | |||
108 | impl<'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 | |||
5 | use itertools::Itertools; | ||
6 | |||
7 | use crate::{ | ||
8 | syntax_node::{SyntaxNodeChildren, SyntaxElementChildren}, | ||
9 | ast::{self, child_opt, children, AstNode, AstToken, AstChildren}, | ||
10 | }; | ||
11 | |||
12 | pub trait TypeAscriptionOwner: AstNode { | ||
13 | fn ascribed_type(&self) -> Option<&ast::TypeRef> { | ||
14 | child_opt(self) | ||
15 | } | ||
16 | } | ||
17 | |||
18 | pub trait NameOwner: AstNode { | ||
19 | fn name(&self) -> Option<&ast::Name> { | ||
20 | child_opt(self) | ||
21 | } | ||
22 | } | ||
23 | |||
24 | pub trait VisibilityOwner: AstNode { | ||
25 | fn visibility(&self) -> Option<&ast::Visibility> { | ||
26 | child_opt(self) | ||
27 | } | ||
28 | } | ||
29 | |||
30 | pub trait LoopBodyOwner: AstNode { | ||
31 | fn loop_body(&self) -> Option<&ast::Block> { | ||
32 | child_opt(self) | ||
33 | } | ||
34 | } | ||
35 | |||
36 | pub trait ArgListOwner: AstNode { | ||
37 | fn arg_list(&self) -> Option<&ast::ArgList> { | ||
38 | child_opt(self) | ||
39 | } | ||
40 | } | ||
41 | |||
42 | pub trait FnDefOwner: AstNode { | ||
43 | fn functions(&self) -> AstChildren<ast::FnDef> { | ||
44 | children(self) | ||
45 | } | ||
46 | } | ||
47 | |||
48 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
49 | pub enum ItemOrMacro<'a> { | ||
50 | Item(&'a ast::ModuleItem), | ||
51 | Macro(&'a ast::MacroCall), | ||
52 | } | ||
53 | |||
54 | pub 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)] | ||
64 | pub struct ItemOrMacroIter<'a>(SyntaxNodeChildren<'a>); | ||
65 | |||
66 | impl<'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 | |||
81 | pub 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 | |||
91 | pub trait TypeBoundsOwner: AstNode { | ||
92 | fn type_bound_list(&self) -> Option<&ast::TypeBoundList> { | ||
93 | child_opt(self) | ||
94 | } | ||
95 | } | ||
96 | |||
97 | pub 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 | |||
106 | pub 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 | |||
145 | pub struct CommentIter<'a> { | ||
146 | iter: SyntaxElementChildren<'a>, | ||
147 | } | ||
148 | |||
149 | impl<'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 | } | ||