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