1 //! Various extension methods to ast Expr Nodes, which are hard to code-generate.
2 //!
3 //! These methods should only do simple, shallow tasks related to the syntax of the node itself.
4
5 use crate::{
6 ast::{
7 self,
8 operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
9 support, AstChildren, AstNode,
10 },
11 AstToken,
12 SyntaxKind::*,
13 SyntaxNode, SyntaxToken, T,
14 };
15
16 impl ast::HasAttrs for ast::Expr {}
17
18 impl ast::Expr {
is_block_like(&self) -> bool19 pub fn is_block_like(&self) -> bool {
20 matches!(
21 self,
22 ast::Expr::IfExpr(_)
23 | ast::Expr::LoopExpr(_)
24 | ast::Expr::ForExpr(_)
25 | ast::Expr::WhileExpr(_)
26 | ast::Expr::BlockExpr(_)
27 | ast::Expr::MatchExpr(_)
28 )
29 }
30 }
31
32 #[derive(Debug, Clone, PartialEq, Eq)]
33 pub enum ElseBranch {
34 Block(ast::BlockExpr),
35 IfExpr(ast::IfExpr),
36 }
37
38 impl From<ast::BlockExpr> for ElseBranch {
from(block_expr: ast::BlockExpr) -> Self39 fn from(block_expr: ast::BlockExpr) -> Self {
40 Self::Block(block_expr)
41 }
42 }
43
44 impl From<ast::IfExpr> for ElseBranch {
from(if_expr: ast::IfExpr) -> Self45 fn from(if_expr: ast::IfExpr) -> Self {
46 Self::IfExpr(if_expr)
47 }
48 }
49
50 impl ast::IfExpr {
then_branch(&self) -> Option<ast::BlockExpr>51 pub fn then_branch(&self) -> Option<ast::BlockExpr> {
52 self.blocks().next()
53 }
54
else_branch(&self) -> Option<ElseBranch>55 pub fn else_branch(&self) -> Option<ElseBranch> {
56 let res = match self.blocks().nth(1) {
57 Some(block) => ElseBranch::Block(block),
58 None => {
59 let elif: ast::IfExpr = support::child(self.syntax())?;
60 ElseBranch::IfExpr(elif)
61 }
62 };
63 Some(res)
64 }
65
blocks(&self) -> AstChildren<ast::BlockExpr>66 pub fn blocks(&self) -> AstChildren<ast::BlockExpr> {
67 support::children(self.syntax())
68 }
69 }
70
71 impl ast::PrefixExpr {
op_kind(&self) -> Option<UnaryOp>72 pub fn op_kind(&self) -> Option<UnaryOp> {
73 let res = match self.op_token()?.kind() {
74 T![*] => UnaryOp::Deref,
75 T![!] => UnaryOp::Not,
76 T![-] => UnaryOp::Neg,
77 _ => return None,
78 };
79 Some(res)
80 }
81
op_token(&self) -> Option<SyntaxToken>82 pub fn op_token(&self) -> Option<SyntaxToken> {
83 self.syntax().first_child_or_token()?.into_token()
84 }
85 }
86
87 impl ast::BinExpr {
op_details(&self) -> Option<(SyntaxToken, BinaryOp)>88 pub fn op_details(&self) -> Option<(SyntaxToken, BinaryOp)> {
89 self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| {
90 #[rustfmt::skip]
91 let bin_op = match c.kind() {
92 T![||] => BinaryOp::LogicOp(LogicOp::Or),
93 T![&&] => BinaryOp::LogicOp(LogicOp::And),
94
95 T![==] => BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
96 T![!=] => BinaryOp::CmpOp(CmpOp::Eq { negated: true }),
97 T![<=] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: false }),
98 T![>=] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: false }),
99 T![<] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: true }),
100 T![>] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: true }),
101
102 T![+] => BinaryOp::ArithOp(ArithOp::Add),
103 T![*] => BinaryOp::ArithOp(ArithOp::Mul),
104 T![-] => BinaryOp::ArithOp(ArithOp::Sub),
105 T![/] => BinaryOp::ArithOp(ArithOp::Div),
106 T![%] => BinaryOp::ArithOp(ArithOp::Rem),
107 T![<<] => BinaryOp::ArithOp(ArithOp::Shl),
108 T![>>] => BinaryOp::ArithOp(ArithOp::Shr),
109 T![^] => BinaryOp::ArithOp(ArithOp::BitXor),
110 T![|] => BinaryOp::ArithOp(ArithOp::BitOr),
111 T![&] => BinaryOp::ArithOp(ArithOp::BitAnd),
112
113 T![=] => BinaryOp::Assignment { op: None },
114 T![+=] => BinaryOp::Assignment { op: Some(ArithOp::Add) },
115 T![*=] => BinaryOp::Assignment { op: Some(ArithOp::Mul) },
116 T![-=] => BinaryOp::Assignment { op: Some(ArithOp::Sub) },
117 T![/=] => BinaryOp::Assignment { op: Some(ArithOp::Div) },
118 T![%=] => BinaryOp::Assignment { op: Some(ArithOp::Rem) },
119 T![<<=] => BinaryOp::Assignment { op: Some(ArithOp::Shl) },
120 T![>>=] => BinaryOp::Assignment { op: Some(ArithOp::Shr) },
121 T![^=] => BinaryOp::Assignment { op: Some(ArithOp::BitXor) },
122 T![|=] => BinaryOp::Assignment { op: Some(ArithOp::BitOr) },
123 T![&=] => BinaryOp::Assignment { op: Some(ArithOp::BitAnd) },
124
125 _ => return None,
126 };
127 Some((c, bin_op))
128 })
129 }
130
op_kind(&self) -> Option<BinaryOp>131 pub fn op_kind(&self) -> Option<BinaryOp> {
132 self.op_details().map(|t| t.1)
133 }
134
op_token(&self) -> Option<SyntaxToken>135 pub fn op_token(&self) -> Option<SyntaxToken> {
136 self.op_details().map(|t| t.0)
137 }
138
lhs(&self) -> Option<ast::Expr>139 pub fn lhs(&self) -> Option<ast::Expr> {
140 support::children(self.syntax()).next()
141 }
142
rhs(&self) -> Option<ast::Expr>143 pub fn rhs(&self) -> Option<ast::Expr> {
144 support::children(self.syntax()).nth(1)
145 }
146
sub_exprs(&self) -> (Option<ast::Expr>, Option<ast::Expr>)147 pub fn sub_exprs(&self) -> (Option<ast::Expr>, Option<ast::Expr>) {
148 let mut children = support::children(self.syntax());
149 let first = children.next();
150 let second = children.next();
151 (first, second)
152 }
153 }
154
155 impl ast::RangeExpr {
op_details(&self) -> Option<(usize, SyntaxToken, RangeOp)>156 fn op_details(&self) -> Option<(usize, SyntaxToken, RangeOp)> {
157 self.syntax().children_with_tokens().enumerate().find_map(|(ix, child)| {
158 let token = child.into_token()?;
159 let bin_op = match token.kind() {
160 T![..] => RangeOp::Exclusive,
161 T![..=] => RangeOp::Inclusive,
162 _ => return None,
163 };
164 Some((ix, token, bin_op))
165 })
166 }
167
op_kind(&self) -> Option<RangeOp>168 pub fn op_kind(&self) -> Option<RangeOp> {
169 self.op_details().map(|t| t.2)
170 }
171
op_token(&self) -> Option<SyntaxToken>172 pub fn op_token(&self) -> Option<SyntaxToken> {
173 self.op_details().map(|t| t.1)
174 }
175
start(&self) -> Option<ast::Expr>176 pub fn start(&self) -> Option<ast::Expr> {
177 let op_ix = self.op_details()?.0;
178 self.syntax()
179 .children_with_tokens()
180 .take(op_ix)
181 .find_map(|it| ast::Expr::cast(it.into_node()?))
182 }
183
end(&self) -> Option<ast::Expr>184 pub fn end(&self) -> Option<ast::Expr> {
185 let op_ix = self.op_details()?.0;
186 self.syntax()
187 .children_with_tokens()
188 .skip(op_ix + 1)
189 .find_map(|it| ast::Expr::cast(it.into_node()?))
190 }
191 }
192
193 impl ast::IndexExpr {
base(&self) -> Option<ast::Expr>194 pub fn base(&self) -> Option<ast::Expr> {
195 support::children(self.syntax()).next()
196 }
index(&self) -> Option<ast::Expr>197 pub fn index(&self) -> Option<ast::Expr> {
198 support::children(self.syntax()).nth(1)
199 }
200 }
201
202 pub enum ArrayExprKind {
203 Repeat { initializer: Option<ast::Expr>, repeat: Option<ast::Expr> },
204 ElementList(AstChildren<ast::Expr>),
205 }
206
207 impl ast::ArrayExpr {
kind(&self) -> ArrayExprKind208 pub fn kind(&self) -> ArrayExprKind {
209 if self.is_repeat() {
210 ArrayExprKind::Repeat {
211 initializer: support::children(self.syntax()).next(),
212 repeat: support::children(self.syntax()).nth(1),
213 }
214 } else {
215 ArrayExprKind::ElementList(support::children(self.syntax()))
216 }
217 }
218
is_repeat(&self) -> bool219 fn is_repeat(&self) -> bool {
220 self.syntax().children_with_tokens().any(|it| it.kind() == T![;])
221 }
222 }
223
224 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
225 pub enum LiteralKind {
226 String(ast::String),
227 ByteString(ast::ByteString),
228 IntNumber(ast::IntNumber),
229 FloatNumber(ast::FloatNumber),
230 Char,
231 Byte,
232 Bool(bool),
233 }
234
235 impl ast::Literal {
token(&self) -> SyntaxToken236 pub fn token(&self) -> SyntaxToken {
237 self.syntax()
238 .children_with_tokens()
239 .find(|e| e.kind() != ATTR && !e.kind().is_trivia())
240 .and_then(|e| e.into_token())
241 .unwrap()
242 }
243
kind(&self) -> LiteralKind244 pub fn kind(&self) -> LiteralKind {
245 let token = self.token();
246
247 if let Some(t) = ast::IntNumber::cast(token.clone()) {
248 return LiteralKind::IntNumber(t);
249 }
250 if let Some(t) = ast::FloatNumber::cast(token.clone()) {
251 return LiteralKind::FloatNumber(t);
252 }
253 if let Some(t) = ast::String::cast(token.clone()) {
254 return LiteralKind::String(t);
255 }
256 if let Some(t) = ast::ByteString::cast(token.clone()) {
257 return LiteralKind::ByteString(t);
258 }
259
260 match token.kind() {
261 T![true] => LiteralKind::Bool(true),
262 T![false] => LiteralKind::Bool(false),
263 CHAR => LiteralKind::Char,
264 BYTE => LiteralKind::Byte,
265 _ => unreachable!(),
266 }
267 }
268 }
269
270 pub enum BlockModifier {
271 Async(SyntaxToken),
272 Unsafe(SyntaxToken),
273 Try(SyntaxToken),
274 Const(SyntaxToken),
275 Label(ast::Label),
276 }
277
278 impl ast::BlockExpr {
modifier(&self) -> Option<BlockModifier>279 pub fn modifier(&self) -> Option<BlockModifier> {
280 self.async_token()
281 .map(BlockModifier::Async)
282 .or_else(|| self.unsafe_token().map(BlockModifier::Unsafe))
283 .or_else(|| self.try_token().map(BlockModifier::Try))
284 .or_else(|| self.const_token().map(BlockModifier::Const))
285 .or_else(|| self.label().map(BlockModifier::Label))
286 }
287 /// false if the block is an intrinsic part of the syntax and can't be
288 /// replaced with arbitrary expression.
289 ///
290 /// ```not_rust
291 /// fn foo() { not_stand_alone }
292 /// const FOO: () = { stand_alone };
293 /// ```
is_standalone(&self) -> bool294 pub fn is_standalone(&self) -> bool {
295 let parent = match self.syntax().parent() {
296 Some(it) => it,
297 None => return true,
298 };
299 !matches!(parent.kind(), FN | IF_EXPR | WHILE_EXPR | LOOP_EXPR)
300 }
301 }
302
303 #[test]
test_literal_with_attr()304 fn test_literal_with_attr() {
305 let parse = ast::SourceFile::parse(r#"const _: &str = { #[attr] "Hello" };"#);
306 let lit = parse.tree().syntax().descendants().find_map(ast::Literal::cast).unwrap();
307 assert_eq!(lit.token().text(), r#""Hello""#);
308 }
309
310 impl ast::RecordExprField {
parent_record_lit(&self) -> ast::RecordExpr311 pub fn parent_record_lit(&self) -> ast::RecordExpr {
312 self.syntax().ancestors().find_map(ast::RecordExpr::cast).unwrap()
313 }
314 }
315
316 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
317 pub enum CallableExpr {
318 Call(ast::CallExpr),
319 MethodCall(ast::MethodCallExpr),
320 }
321
322 impl ast::HasAttrs for CallableExpr {}
323 impl ast::HasArgList for CallableExpr {}
324
325 impl AstNode for CallableExpr {
can_cast(kind: parser::SyntaxKind) -> bool where Self: Sized,326 fn can_cast(kind: parser::SyntaxKind) -> bool
327 where
328 Self: Sized,
329 {
330 ast::CallExpr::can_cast(kind) || ast::MethodCallExpr::can_cast(kind)
331 }
332
cast(syntax: SyntaxNode) -> Option<Self> where Self: Sized,333 fn cast(syntax: SyntaxNode) -> Option<Self>
334 where
335 Self: Sized,
336 {
337 if let Some(it) = ast::CallExpr::cast(syntax.clone()) {
338 Some(Self::Call(it))
339 } else if let Some(it) = ast::MethodCallExpr::cast(syntax) {
340 Some(Self::MethodCall(it))
341 } else {
342 None
343 }
344 }
345
syntax(&self) -> &SyntaxNode346 fn syntax(&self) -> &SyntaxNode {
347 match self {
348 Self::Call(it) => it.syntax(),
349 Self::MethodCall(it) => it.syntax(),
350 }
351 }
352 }
353