rust/crates/syntax/src/ast/expr_ext.rs

331 lines
10 KiB
Rust
Raw Normal View History

2019-04-02 10:02:23 +00:00
//! Various extension methods to ast Expr Nodes, which are hard to code-generate.
//!
//! These methods should only do simple, shallow tasks related to the syntax of the node itself.
2021-07-29 20:01:24 +00:00
2019-04-02 09:47:39 +00:00
use crate::{
ast::{
self,
operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
support, AstChildren, AstNode,
},
2020-11-06 21:52:22 +00:00
AstToken,
2019-05-15 12:35:47 +00:00
SyntaxKind::*,
SyntaxToken, T,
2019-04-02 09:47:39 +00:00
};
impl ast::AttrsOwner for ast::Expr {}
impl ast::Expr {
pub fn is_block_like(&self) -> bool {
2021-03-21 14:33:18 +00:00
matches!(
self,
ast::Expr::IfExpr(_)
2021-03-21 14:33:18 +00:00
| ast::Expr::LoopExpr(_)
| ast::Expr::ForExpr(_)
| ast::Expr::WhileExpr(_)
| ast::Expr::BlockExpr(_)
| ast::Expr::MatchExpr(_)
| ast::Expr::EffectExpr(_)
)
}
}
2019-04-02 09:47:39 +00:00
#[derive(Debug, Clone, PartialEq, Eq)]
2019-07-18 16:23:05 +00:00
pub enum ElseBranch {
2019-09-02 18:23:19 +00:00
Block(ast::BlockExpr),
2019-07-18 16:23:05 +00:00
IfExpr(ast::IfExpr),
2019-04-02 09:47:39 +00:00
}
2021-08-10 20:08:14 +00:00
impl From<ast::BlockExpr> for ElseBranch {
fn from(block_expr: ast::BlockExpr) -> Self {
Self::Block(block_expr)
}
}
impl From<ast::IfExpr> for ElseBranch {
fn from(if_expr: ast::IfExpr) -> Self {
Self::IfExpr(if_expr)
}
}
2019-04-02 09:47:39 +00:00
impl ast::IfExpr {
2019-09-02 18:23:19 +00:00
pub fn then_branch(&self) -> Option<ast::BlockExpr> {
self.blocks().next()
2019-04-02 09:47:39 +00:00
}
2019-04-02 09:47:39 +00:00
pub fn else_branch(&self) -> Option<ElseBranch> {
let res = match self.blocks().nth(1) {
Some(block) => ElseBranch::Block(block),
None => {
2020-04-09 20:22:58 +00:00
let elif: ast::IfExpr = support::child(self.syntax())?;
2019-04-02 09:47:39 +00:00
ElseBranch::IfExpr(elif)
}
};
Some(res)
}
pub fn blocks(&self) -> AstChildren<ast::BlockExpr> {
2020-04-09 20:22:58 +00:00
support::children(self.syntax())
2019-04-02 09:47:39 +00:00
}
}
impl ast::PrefixExpr {
2021-08-14 14:01:28 +00:00
pub fn op_kind(&self) -> Option<UnaryOp> {
let res = match self.op_token()?.kind() {
2021-08-14 14:01:28 +00:00
T![*] => UnaryOp::Deref,
T![!] => UnaryOp::Not,
T![-] => UnaryOp::Neg,
_ => return None,
};
Some(res)
2019-04-02 09:47:39 +00:00
}
pub fn op_token(&self) -> Option<SyntaxToken> {
2019-07-19 16:05:34 +00:00
self.syntax().first_child_or_token()?.into_token()
2019-04-02 09:47:39 +00:00
}
}
impl ast::BinExpr {
pub fn op_details(&self) -> Option<(SyntaxToken, BinaryOp)> {
2019-08-17 14:14:22 +00:00
self.syntax().children_with_tokens().filter_map(|it| it.into_token()).find_map(|c| {
#[rustfmt::skip]
2019-08-17 14:14:22 +00:00
let bin_op = match c.kind() {
T![||] => BinaryOp::LogicOp(LogicOp::Or),
T![&&] => BinaryOp::LogicOp(LogicOp::And),
T![==] => BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
T![!=] => BinaryOp::CmpOp(CmpOp::Eq { negated: true }),
T![<=] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: false }),
T![>=] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: false }),
T![<] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Less, strict: true }),
T![>] => BinaryOp::CmpOp(CmpOp::Ord { ordering: Ordering::Greater, strict: true }),
T![+] => BinaryOp::ArithOp(ArithOp::Add),
T![*] => BinaryOp::ArithOp(ArithOp::Mul),
T![-] => BinaryOp::ArithOp(ArithOp::Sub),
T![/] => BinaryOp::ArithOp(ArithOp::Div),
T![%] => BinaryOp::ArithOp(ArithOp::Rem),
T![<<] => BinaryOp::ArithOp(ArithOp::Shl),
T![>>] => BinaryOp::ArithOp(ArithOp::Shr),
T![^] => BinaryOp::ArithOp(ArithOp::BitXor),
T![|] => BinaryOp::ArithOp(ArithOp::BitOr),
T![&] => BinaryOp::ArithOp(ArithOp::BitAnd),
T![=] => BinaryOp::Assignment { op: None },
T![+=] => BinaryOp::Assignment { op: Some(ArithOp::Add) },
T![*=] => BinaryOp::Assignment { op: Some(ArithOp::Mul) },
T![-=] => BinaryOp::Assignment { op: Some(ArithOp::Sub) },
T![/=] => BinaryOp::Assignment { op: Some(ArithOp::Div) },
T![%=] => BinaryOp::Assignment { op: Some(ArithOp::Rem) },
T![<<=] => BinaryOp::Assignment { op: Some(ArithOp::Shl) },
T![>>=] => BinaryOp::Assignment { op: Some(ArithOp::Shr) },
T![^=] => BinaryOp::Assignment { op: Some(ArithOp::BitXor) },
T![|=] => BinaryOp::Assignment { op: Some(ArithOp::BitOr) },
T![&=] => BinaryOp::Assignment { op: Some(ArithOp::BitAnd) },
2019-08-17 14:14:22 +00:00
_ => return None,
};
Some((c, bin_op))
2019-04-02 09:47:39 +00:00
})
}
pub fn op_kind(&self) -> Option<BinaryOp> {
2019-04-02 09:47:39 +00:00
self.op_details().map(|t| t.1)
}
pub fn op_token(&self) -> Option<SyntaxToken> {
self.op_details().map(|t| t.0)
}
2019-07-18 16:23:05 +00:00
pub fn lhs(&self) -> Option<ast::Expr> {
2020-04-09 20:22:58 +00:00
support::children(self.syntax()).next()
2019-04-02 09:47:39 +00:00
}
2019-07-18 16:23:05 +00:00
pub fn rhs(&self) -> Option<ast::Expr> {
2020-04-09 20:22:58 +00:00
support::children(self.syntax()).nth(1)
2019-04-02 09:47:39 +00:00
}
2019-07-18 16:23:05 +00:00
pub fn sub_exprs(&self) -> (Option<ast::Expr>, Option<ast::Expr>) {
2020-04-09 20:22:58 +00:00
let mut children = support::children(self.syntax());
2019-04-02 09:47:39 +00:00
let first = children.next();
let second = children.next();
(first, second)
}
}
2019-11-15 20:05:29 +00:00
impl ast::RangeExpr {
fn op_details(&self) -> Option<(usize, SyntaxToken, RangeOp)> {
self.syntax().children_with_tokens().enumerate().find_map(|(ix, child)| {
let token = child.into_token()?;
let bin_op = match token.kind() {
T![..] => RangeOp::Exclusive,
T![..=] => RangeOp::Inclusive,
_ => return None,
};
Some((ix, token, bin_op))
})
}
pub fn op_kind(&self) -> Option<RangeOp> {
self.op_details().map(|t| t.2)
}
pub fn op_token(&self) -> Option<SyntaxToken> {
self.op_details().map(|t| t.1)
}
pub fn start(&self) -> Option<ast::Expr> {
let op_ix = self.op_details()?.0;
self.syntax()
.children_with_tokens()
.take(op_ix)
.find_map(|it| ast::Expr::cast(it.into_node()?))
}
pub fn end(&self) -> Option<ast::Expr> {
let op_ix = self.op_details()?.0;
self.syntax()
.children_with_tokens()
.skip(op_ix + 1)
.find_map(|it| ast::Expr::cast(it.into_node()?))
}
}
2019-08-17 14:17:01 +00:00
impl ast::IndexExpr {
pub fn base(&self) -> Option<ast::Expr> {
2020-04-09 20:22:58 +00:00
support::children(self.syntax()).next()
2019-08-17 14:17:01 +00:00
}
pub fn index(&self) -> Option<ast::Expr> {
2020-04-09 20:22:58 +00:00
support::children(self.syntax()).nth(1)
2019-08-17 14:17:01 +00:00
}
}
2019-07-18 16:23:05 +00:00
pub enum ArrayExprKind {
Repeat { initializer: Option<ast::Expr>, repeat: Option<ast::Expr> },
ElementList(AstChildren<ast::Expr>),
}
impl ast::ArrayExpr {
pub fn kind(&self) -> ArrayExprKind {
if self.is_repeat() {
ArrayExprKind::Repeat {
2020-04-09 20:22:58 +00:00
initializer: support::children(self.syntax()).next(),
repeat: support::children(self.syntax()).nth(1),
}
} else {
2020-04-09 20:22:58 +00:00
ArrayExprKind::ElementList(support::children(self.syntax()))
}
}
fn is_repeat(&self) -> bool {
2019-05-15 12:35:47 +00:00
self.syntax().children_with_tokens().any(|it| it.kind() == T![;])
}
}
2019-04-02 09:47:39 +00:00
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
2019-04-02 09:48:14 +00:00
pub enum LiteralKind {
2020-11-06 21:52:22 +00:00
String(ast::String),
ByteString(ast::ByteString),
IntNumber(ast::IntNumber),
FloatNumber(ast::FloatNumber),
2019-04-02 09:47:39 +00:00
Char,
Byte,
Bool(bool),
2019-04-02 09:47:39 +00:00
}
impl ast::Literal {
pub fn token(&self) -> SyntaxToken {
2019-07-20 17:04:34 +00:00
self.syntax()
2019-06-15 13:22:31 +00:00
.children_with_tokens()
2019-07-20 17:04:34 +00:00
.find(|e| e.kind() != ATTR && !e.kind().is_trivia())
.and_then(|e| e.into_token())
.unwrap()
2019-04-02 09:47:39 +00:00
}
pub fn kind(&self) -> LiteralKind {
let token = self.token();
2020-11-06 21:52:22 +00:00
if let Some(t) = ast::IntNumber::cast(token.clone()) {
return LiteralKind::IntNumber(t);
}
if let Some(t) = ast::FloatNumber::cast(token.clone()) {
return LiteralKind::FloatNumber(t);
}
if let Some(t) = ast::String::cast(token.clone()) {
return LiteralKind::String(t);
}
if let Some(t) = ast::ByteString::cast(token.clone()) {
return LiteralKind::ByteString(t);
}
match token.kind() {
T![true] => LiteralKind::Bool(true),
T![false] => LiteralKind::Bool(false),
2019-04-02 09:48:14 +00:00
CHAR => LiteralKind::Char,
BYTE => LiteralKind::Byte,
2019-04-02 09:47:39 +00:00
_ => unreachable!(),
}
}
}
2020-05-01 23:18:19 +00:00
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Effect {
2020-04-30 20:43:06 +00:00
Async(SyntaxToken),
Unsafe(SyntaxToken),
2020-05-01 23:18:19 +00:00
Try(SyntaxToken),
2020-12-23 11:24:24 +00:00
Const(SyntaxToken),
2020-05-01 23:18:19 +00:00
// Very much not an effect, but we stuff it into this node anyway
Label(ast::Label),
2020-04-30 20:43:06 +00:00
}
2020-05-01 23:18:19 +00:00
impl ast::EffectExpr {
pub fn effect(&self) -> Effect {
2020-04-30 20:43:06 +00:00
if let Some(token) = self.async_token() {
2020-05-01 23:18:19 +00:00
return Effect::Async(token);
2020-04-30 20:43:06 +00:00
}
if let Some(token) = self.unsafe_token() {
2020-05-01 23:18:19 +00:00
return Effect::Unsafe(token);
}
if let Some(token) = self.try_token() {
return Effect::Try(token);
}
2020-12-23 11:24:24 +00:00
if let Some(token) = self.const_token() {
return Effect::Const(token);
}
2020-05-01 23:18:19 +00:00
if let Some(label) = self.label() {
return Effect::Label(label);
2020-04-30 20:43:06 +00:00
}
2020-05-01 23:18:19 +00:00
unreachable!("ast::EffectExpr without Effect")
2020-04-30 20:43:06 +00:00
}
2020-05-01 23:18:19 +00:00
}
2020-04-30 20:43:06 +00:00
2019-09-02 18:41:50 +00:00
impl ast::BlockExpr {
/// false if the block is an intrinsic part of the syntax and can't be
/// replaced with arbitrary expression.
///
/// ```not_rust
/// fn foo() { not_stand_alone }
/// const FOO: () = { stand_alone };
/// ```
pub fn is_standalone(&self) -> bool {
let parent = match self.syntax().parent() {
Some(it) => it,
2019-09-02 18:41:50 +00:00
None => return true,
};
2020-07-30 12:51:08 +00:00
!matches!(parent.kind(), FN | IF_EXPR | WHILE_EXPR | LOOP_EXPR | EFFECT_EXPR)
2019-09-02 18:41:50 +00:00
}
}
2019-06-15 13:22:31 +00:00
#[test]
fn test_literal_with_attr() {
let parse = ast::SourceFile::parse(r#"const _: &str = { #[attr] "Hello" };"#);
2019-07-18 16:23:05 +00:00
let lit = parse.tree().syntax().descendants().find_map(ast::Literal::cast).unwrap();
2019-06-15 13:22:31 +00:00
assert_eq!(lit.token().text(), r#""Hello""#);
}
2020-07-30 14:21:30 +00:00
impl ast::RecordExprField {
pub fn parent_record_lit(&self) -> ast::RecordExpr {
self.syntax().ancestors().find_map(ast::RecordExpr::cast).unwrap()
2019-04-02 09:47:39 +00:00
}
}