diff options
Diffstat (limited to 'crates/ra_syntax/src/ast')
-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 | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/traits.rs | 4 |
4 files changed, 561 insertions, 0 deletions
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 index 7c30ff15c..da7d507bf 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | //! There are many AstNodes, but only a few tokens, so we hand-write them here. | ||
2 | |||
1 | use crate::{ | 3 | use crate::{ |
2 | SyntaxToken, | 4 | SyntaxToken, |
3 | SyntaxKind::{COMMENT, WHITESPACE}, | 5 | SyntaxKind::{COMMENT, WHITESPACE}, |
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index 98aa22085..aaf07d731 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs | |||
@@ -1,3 +1,7 @@ | |||
1 | //! Various traits that are implemented by ast nodes. | ||
2 | //! | ||
3 | //! The implementations are usually trivial, and live in generated.rs | ||
4 | |||
1 | use itertools::Itertools; | 5 | use itertools::Itertools; |
2 | 6 | ||
3 | use crate::{ | 7 | use crate::{ |