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