diff options
Diffstat (limited to 'crates/ra_syntax/src/ast/extensions.rs')
-rw-r--r-- | crates/ra_syntax/src/ast/extensions.rs | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs new file mode 100644 index 000000000..87592bfd8 --- /dev/null +++ b/crates/ra_syntax/src/ast/extensions.rs | |||
@@ -0,0 +1,300 @@ | |||
1 | use itertools::Itertools; | ||
2 | |||
3 | use crate::{ | ||
4 | SmolStr, SyntaxToken, | ||
5 | ast::{self, AstNode, children, child_opt}, | ||
6 | SyntaxKind::*, | ||
7 | }; | ||
8 | |||
9 | impl ast::Name { | ||
10 | pub fn text(&self) -> &SmolStr { | ||
11 | let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap(); | ||
12 | ident.text() | ||
13 | } | ||
14 | } | ||
15 | |||
16 | impl ast::NameRef { | ||
17 | pub fn text(&self) -> &SmolStr { | ||
18 | let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap(); | ||
19 | ident.text() | ||
20 | } | ||
21 | } | ||
22 | |||
23 | impl ast::Attr { | ||
24 | pub fn is_inner(&self) -> bool { | ||
25 | let tt = match self.value() { | ||
26 | None => return false, | ||
27 | Some(tt) => tt, | ||
28 | }; | ||
29 | |||
30 | let prev = match tt.syntax().prev_sibling() { | ||
31 | None => return false, | ||
32 | Some(prev) => prev, | ||
33 | }; | ||
34 | |||
35 | prev.kind() == EXCL | ||
36 | } | ||
37 | |||
38 | pub fn as_atom(&self) -> Option<SmolStr> { | ||
39 | let tt = self.value()?; | ||
40 | let (_bra, attr, _ket) = tt.syntax().children_with_tokens().collect_tuple()?; | ||
41 | if attr.kind() == IDENT { | ||
42 | Some(attr.as_token()?.text().clone()) | ||
43 | } else { | ||
44 | None | ||
45 | } | ||
46 | } | ||
47 | |||
48 | pub fn as_call(&self) -> Option<(SmolStr, &ast::TokenTree)> { | ||
49 | let tt = self.value()?; | ||
50 | let (_bra, attr, args, _ket) = tt.syntax().children_with_tokens().collect_tuple()?; | ||
51 | let args = ast::TokenTree::cast(args.as_node()?)?; | ||
52 | if attr.kind() == IDENT { | ||
53 | Some((attr.as_token()?.text().clone(), args)) | ||
54 | } else { | ||
55 | None | ||
56 | } | ||
57 | } | ||
58 | |||
59 | pub fn as_named(&self) -> Option<SmolStr> { | ||
60 | let tt = self.value()?; | ||
61 | let attr = tt.syntax().children_with_tokens().nth(1)?; | ||
62 | if attr.kind() == IDENT { | ||
63 | Some(attr.as_token()?.text().clone()) | ||
64 | } else { | ||
65 | None | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
71 | pub enum PathSegmentKind<'a> { | ||
72 | Name(&'a ast::NameRef), | ||
73 | SelfKw, | ||
74 | SuperKw, | ||
75 | CrateKw, | ||
76 | } | ||
77 | |||
78 | impl ast::PathSegment { | ||
79 | pub fn parent_path(&self) -> &ast::Path { | ||
80 | self.syntax() | ||
81 | .parent() | ||
82 | .and_then(ast::Path::cast) | ||
83 | .expect("segments are always nested in paths") | ||
84 | } | ||
85 | |||
86 | pub fn kind(&self) -> Option<PathSegmentKind> { | ||
87 | let res = if let Some(name_ref) = self.name_ref() { | ||
88 | PathSegmentKind::Name(name_ref) | ||
89 | } else { | ||
90 | match self.syntax().first_child_or_token()?.kind() { | ||
91 | SELF_KW => PathSegmentKind::SelfKw, | ||
92 | SUPER_KW => PathSegmentKind::SuperKw, | ||
93 | CRATE_KW => PathSegmentKind::CrateKw, | ||
94 | _ => return None, | ||
95 | } | ||
96 | }; | ||
97 | Some(res) | ||
98 | } | ||
99 | |||
100 | pub fn has_colon_colon(&self) -> bool { | ||
101 | match self.syntax.first_child_or_token().map(|s| s.kind()) { | ||
102 | Some(COLONCOLON) => true, | ||
103 | _ => false, | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | impl ast::Path { | ||
109 | pub fn parent_path(&self) -> Option<&ast::Path> { | ||
110 | self.syntax().parent().and_then(ast::Path::cast) | ||
111 | } | ||
112 | } | ||
113 | |||
114 | impl ast::Module { | ||
115 | pub fn has_semi(&self) -> bool { | ||
116 | match self.syntax().last_child_or_token() { | ||
117 | None => false, | ||
118 | Some(node) => node.kind() == SEMI, | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | |||
123 | impl ast::UseTree { | ||
124 | pub fn has_star(&self) -> bool { | ||
125 | self.syntax().children_with_tokens().any(|it| it.kind() == STAR) | ||
126 | } | ||
127 | } | ||
128 | |||
129 | impl ast::UseTreeList { | ||
130 | pub fn parent_use_tree(&self) -> &ast::UseTree { | ||
131 | self.syntax() | ||
132 | .parent() | ||
133 | .and_then(ast::UseTree::cast) | ||
134 | .expect("UseTreeLists are always nested in UseTrees") | ||
135 | } | ||
136 | } | ||
137 | |||
138 | impl ast::ImplBlock { | ||
139 | pub fn target_type(&self) -> Option<&ast::TypeRef> { | ||
140 | match self.target() { | ||
141 | (Some(t), None) | (_, Some(t)) => Some(t), | ||
142 | _ => None, | ||
143 | } | ||
144 | } | ||
145 | |||
146 | pub fn target_trait(&self) -> Option<&ast::TypeRef> { | ||
147 | match self.target() { | ||
148 | (Some(t), Some(_)) => Some(t), | ||
149 | _ => None, | ||
150 | } | ||
151 | } | ||
152 | |||
153 | fn target(&self) -> (Option<&ast::TypeRef>, Option<&ast::TypeRef>) { | ||
154 | let mut types = children(self); | ||
155 | let first = types.next(); | ||
156 | let second = types.next(); | ||
157 | (first, second) | ||
158 | } | ||
159 | } | ||
160 | |||
161 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
162 | pub enum StructFlavor<'a> { | ||
163 | Tuple(&'a ast::PosFieldDefList), | ||
164 | Named(&'a ast::NamedFieldDefList), | ||
165 | Unit, | ||
166 | } | ||
167 | |||
168 | impl StructFlavor<'_> { | ||
169 | fn from_node<N: AstNode>(node: &N) -> StructFlavor { | ||
170 | if let Some(nfdl) = child_opt::<_, ast::NamedFieldDefList>(node) { | ||
171 | StructFlavor::Named(nfdl) | ||
172 | } else if let Some(pfl) = child_opt::<_, ast::PosFieldDefList>(node) { | ||
173 | StructFlavor::Tuple(pfl) | ||
174 | } else { | ||
175 | StructFlavor::Unit | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | |||
180 | impl ast::StructDef { | ||
181 | pub fn flavor(&self) -> StructFlavor { | ||
182 | StructFlavor::from_node(self) | ||
183 | } | ||
184 | } | ||
185 | |||
186 | impl ast::EnumVariant { | ||
187 | pub fn parent_enum(&self) -> &ast::EnumDef { | ||
188 | self.syntax() | ||
189 | .parent() | ||
190 | .and_then(|it| it.parent()) | ||
191 | .and_then(ast::EnumDef::cast) | ||
192 | .expect("EnumVariants are always nested in Enums") | ||
193 | } | ||
194 | pub fn flavor(&self) -> StructFlavor { | ||
195 | StructFlavor::from_node(self) | ||
196 | } | ||
197 | } | ||
198 | |||
199 | impl ast::LetStmt { | ||
200 | pub fn has_semi(&self) -> bool { | ||
201 | match self.syntax().last_child_or_token() { | ||
202 | None => false, | ||
203 | Some(node) => node.kind() == SEMI, | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | |||
208 | impl ast::ExprStmt { | ||
209 | pub fn has_semi(&self) -> bool { | ||
210 | match self.syntax().last_child_or_token() { | ||
211 | None => false, | ||
212 | Some(node) => node.kind() == SEMI, | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | |||
217 | impl ast::RefPat { | ||
218 | pub fn is_mut(&self) -> bool { | ||
219 | self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW) | ||
220 | } | ||
221 | } | ||
222 | |||
223 | impl ast::BindPat { | ||
224 | pub fn is_mutable(&self) -> bool { | ||
225 | self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW) | ||
226 | } | ||
227 | |||
228 | pub fn is_ref(&self) -> bool { | ||
229 | self.syntax().children_with_tokens().any(|n| n.kind() == REF_KW) | ||
230 | } | ||
231 | } | ||
232 | |||
233 | impl ast::PointerType { | ||
234 | pub fn is_mut(&self) -> bool { | ||
235 | self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW) | ||
236 | } | ||
237 | } | ||
238 | |||
239 | impl ast::ReferenceType { | ||
240 | pub fn is_mut(&self) -> bool { | ||
241 | self.syntax().children_with_tokens().any(|n| n.kind() == MUT_KW) | ||
242 | } | ||
243 | } | ||
244 | |||
245 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] | ||
246 | pub enum SelfParamFlavor { | ||
247 | /// self | ||
248 | Owned, | ||
249 | /// &self | ||
250 | Ref, | ||
251 | /// &mut self | ||
252 | MutRef, | ||
253 | } | ||
254 | |||
255 | impl ast::SelfParam { | ||
256 | pub fn self_kw_token(&self) -> SyntaxToken { | ||
257 | self.syntax() | ||
258 | .children_with_tokens() | ||
259 | .filter_map(|it| it.as_token()) | ||
260 | .find(|it| it.kind() == SELF_KW) | ||
261 | .expect("invalid tree: self param must have self") | ||
262 | } | ||
263 | |||
264 | pub fn flavor(&self) -> SelfParamFlavor { | ||
265 | let borrowed = self.syntax().children_with_tokens().any(|n| n.kind() == AMP); | ||
266 | if borrowed { | ||
267 | // check for a `mut` coming after the & -- `mut &self` != `&mut self` | ||
268 | if self | ||
269 | .syntax() | ||
270 | .children_with_tokens() | ||
271 | .skip_while(|n| n.kind() != AMP) | ||
272 | .any(|n| n.kind() == MUT_KW) | ||
273 | { | ||
274 | SelfParamFlavor::MutRef | ||
275 | } else { | ||
276 | SelfParamFlavor::Ref | ||
277 | } | ||
278 | } else { | ||
279 | SelfParamFlavor::Owned | ||
280 | } | ||
281 | } | ||
282 | } | ||
283 | |||
284 | impl ast::LifetimeParam { | ||
285 | pub fn lifetime_token(&self) -> Option<SyntaxToken> { | ||
286 | self.syntax() | ||
287 | .children_with_tokens() | ||
288 | .filter_map(|it| it.as_token()) | ||
289 | .find(|it| it.kind() == LIFETIME) | ||
290 | } | ||
291 | } | ||
292 | |||
293 | impl ast::WherePred { | ||
294 | pub fn lifetime_token(&self) -> Option<SyntaxToken> { | ||
295 | self.syntax() | ||
296 | .children_with_tokens() | ||
297 | .filter_map(|it| it.as_token()) | ||
298 | .find(|it| it.kind() == LIFETIME) | ||
299 | } | ||
300 | } | ||