use rowan::ast::{AstChildren, AstNode, support};
use super::tokens::{AstToken, Ident, Operator, child_token};
use crate::syntax::{JuliaLanguage, SyntaxKind, SyntaxNode, SyntaxToken};
macro_rules! ast_node {
($(#[$meta:meta])* $name:ident, $kind:expr) => {
$(#[$meta])*
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct $name(SyntaxNode);
impl AstNode for $name {
type Language = JuliaLanguage;
fn can_cast(kind: SyntaxKind) -> bool {
kind == $kind
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
Self::can_cast(syntax.kind()).then_some(Self(syntax))
}
fn syntax(&self) -> &SyntaxNode {
&self.0
}
}
};
}
ast_node!(Root, SyntaxKind::ROOT);
ast_node!(Literal, SyntaxKind::LITERAL);
ast_node!(StringLiteral, SyntaxKind::STRING_LITERAL);
ast_node!(CmdLiteral, SyntaxKind::CMD_LITERAL);
ast_node!(NonstandardIdentifier, SyntaxKind::NONSTANDARD_IDENTIFIER);
ast_node!(Interpolation, SyntaxKind::INTERPOLATION);
ast_node!(Name, SyntaxKind::NAME);
ast_node!(BinaryExpr, SyntaxKind::BINARY_EXPR);
ast_node!(UnaryExpr, SyntaxKind::UNARY_EXPR);
ast_node!(ParenExpr, SyntaxKind::PAREN_EXPR);
ast_node!(TupleExpr, SyntaxKind::TUPLE_EXPR);
ast_node!(VectExpr, SyntaxKind::VECT_EXPR);
ast_node!(MatrixExpr, SyntaxKind::MATRIX_EXPR);
ast_node!(MatrixRow, SyntaxKind::MATRIX_ROW);
ast_node!(Comprehension, SyntaxKind::COMPREHENSION);
ast_node!(Generator, SyntaxKind::GENERATOR);
ast_node!(ComprehensionIf, SyntaxKind::COMPREHENSION_IF);
ast_node!(CallExpr, SyntaxKind::CALL_EXPR);
ast_node!(IndexExpr, SyntaxKind::INDEX_EXPR);
ast_node!(DotCallExpr, SyntaxKind::DOT_CALL_EXPR);
ast_node!(CurlyExpr, SyntaxKind::CURLY_EXPR);
ast_node!(Braces, SyntaxKind::BRACES);
ast_node!(ArgList, SyntaxKind::ARG_LIST);
ast_node!(Arg, SyntaxKind::ARG);
ast_node!(KeywordArg, SyntaxKind::KEYWORD_ARG);
ast_node!(Parameters, SyntaxKind::PARAMETERS);
ast_node!(TypeAnnotation, SyntaxKind::TYPE_ANNOTATION);
ast_node!(WhereExpr, SyntaxKind::WHERE_EXPR);
ast_node!(SplatExpr, SyntaxKind::SPLAT_EXPR);
ast_node!(EndMarker, SyntaxKind::END_MARKER);
ast_node!(AssignmentExpr, SyntaxKind::ASSIGNMENT_EXPR);
ast_node!(ArrowExpr, SyntaxKind::ARROW_EXPR);
ast_node!(TernaryExpr, SyntaxKind::TERNARY_EXPR);
ast_node!(IfExpr, SyntaxKind::IF_EXPR);
ast_node!(ElseifClause, SyntaxKind::ELSEIF_CLAUSE);
ast_node!(ElseClause, SyntaxKind::ELSE_CLAUSE);
ast_node!(Condition, SyntaxKind::CONDITION);
ast_node!(FunctionDef, SyntaxKind::FUNCTION_DEF);
ast_node!(MacroDef, SyntaxKind::MACRO_DEF);
ast_node!(Signature, SyntaxKind::SIGNATURE);
ast_node!(Block, SyntaxKind::BLOCK);
ast_node!(BeginExpr, SyntaxKind::BEGIN_EXPR);
ast_node!(QuoteExpr, SyntaxKind::QUOTE_EXPR);
ast_node!(QuoteSym, SyntaxKind::QUOTE_SYM);
ast_node!(WhileExpr, SyntaxKind::WHILE_EXPR);
ast_node!(ForExpr, SyntaxKind::FOR_EXPR);
ast_node!(ForBinding, SyntaxKind::FOR_BINDING);
ast_node!(LetExpr, SyntaxKind::LET_EXPR);
ast_node!(LetBindings, SyntaxKind::LET_BINDINGS);
ast_node!(TryExpr, SyntaxKind::TRY_EXPR);
ast_node!(CatchClause, SyntaxKind::CATCH_CLAUSE);
ast_node!(FinallyClause, SyntaxKind::FINALLY_CLAUSE);
ast_node!(StructDef, SyntaxKind::STRUCT_DEF);
ast_node!(AbstractDef, SyntaxKind::ABSTRACT_DEF);
ast_node!(PrimitiveDef, SyntaxKind::PRIMITIVE_DEF);
ast_node!(ModuleDef, SyntaxKind::MODULE_DEF);
ast_node!(DoExpr, SyntaxKind::DO_EXPR);
ast_node!(DoParams, SyntaxKind::DO_PARAMS);
ast_node!(ReturnExpr, SyntaxKind::RETURN_EXPR);
ast_node!(BreakExpr, SyntaxKind::BREAK_EXPR);
ast_node!(ContinueExpr, SyntaxKind::CONTINUE_EXPR);
ast_node!(ConstStmt, SyntaxKind::CONST_STMT);
ast_node!(GlobalStmt, SyntaxKind::GLOBAL_STMT);
ast_node!(LocalStmt, SyntaxKind::LOCAL_STMT);
ast_node!(ImportStmt, SyntaxKind::IMPORT_STMT);
ast_node!(UsingStmt, SyntaxKind::USING_STMT);
ast_node!(ExportStmt, SyntaxKind::EXPORT_STMT);
ast_node!(MacroCall, SyntaxKind::MACRO_CALL);
ast_node!(MacroName, SyntaxKind::MACRO_NAME);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expr {
Literal(Literal),
StringLiteral(StringLiteral),
CmdLiteral(CmdLiteral),
NonstandardIdentifier(NonstandardIdentifier),
Interpolation(Interpolation),
Name(Name),
BinaryExpr(BinaryExpr),
UnaryExpr(UnaryExpr),
ParenExpr(ParenExpr),
TupleExpr(TupleExpr),
VectExpr(VectExpr),
MatrixExpr(MatrixExpr),
Comprehension(Comprehension),
Generator(Generator),
CallExpr(CallExpr),
IndexExpr(IndexExpr),
DotCallExpr(DotCallExpr),
CurlyExpr(CurlyExpr),
Braces(Braces),
AssignmentExpr(AssignmentExpr),
ArrowExpr(ArrowExpr),
TernaryExpr(TernaryExpr),
IfExpr(IfExpr),
BeginExpr(BeginExpr),
QuoteExpr(QuoteExpr),
WhileExpr(WhileExpr),
ForExpr(ForExpr),
LetExpr(LetExpr),
TryExpr(TryExpr),
DoExpr(DoExpr),
FunctionDef(FunctionDef),
MacroCall(MacroCall),
SplatExpr(SplatExpr),
WhereExpr(WhereExpr),
TypeAnnotation(TypeAnnotation),
ReturnExpr(ReturnExpr),
BreakExpr(BreakExpr),
ContinueExpr(ContinueExpr),
Other(SyntaxNode),
}
pub fn is_expr_kind(kind: SyntaxKind) -> bool {
use SyntaxKind::*;
matches!(
kind,
LITERAL
| STRING_LITERAL
| CMD_LITERAL
| NONSTANDARD_IDENTIFIER
| INTERPOLATION
| NAME
| BINARY_EXPR
| RANGE_EXPR
| COMPARISON_EXPR
| UNARY_EXPR
| PAREN_EXPR
| TUPLE_EXPR
| BARE_TUPLE_EXPR
| PAREN_BLOCK
| VECT_EXPR
| MATRIX_EXPR
| TYPED_MATRIX_EXPR
| BRACESCAT_EXPR
| COMPREHENSION
| BRACES_COMPREHENSION
| TYPED_COMPREHENSION
| GENERATOR
| CALL_EXPR
| INDEX_EXPR
| DOT_CALL_EXPR
| CURLY_EXPR
| BRACES
| TYPE_ANNOTATION
| WHERE_EXPR
| SPLAT_EXPR
| POSTFIX_EXPR
| OPERATOR_ATOM
| ASSIGNMENT_EXPR
| ARROW_EXPR
| JUXTAPOSE_EXPR
| TERNARY_EXPR
| IF_EXPR
| FUNCTION_DEF
| BEGIN_EXPR
| WHILE_EXPR
| FOR_EXPR
| LET_EXPR
| QUOTE_EXPR
| QUOTE_SYM
| TRY_EXPR
| STRUCT_DEF
| ABSTRACT_DEF
| PRIMITIVE_DEF
| MODULE_DEF
| DO_EXPR
| RETURN_EXPR
| BREAK_EXPR
| CONTINUE_EXPR
| MACRO_CALL
)
}
impl AstNode for Expr {
type Language = JuliaLanguage;
fn can_cast(kind: SyntaxKind) -> bool {
is_expr_kind(kind)
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
let expr = match syntax.kind() {
SyntaxKind::LITERAL => Expr::Literal(Literal(syntax)),
SyntaxKind::STRING_LITERAL => Expr::StringLiteral(StringLiteral(syntax)),
SyntaxKind::CMD_LITERAL => Expr::CmdLiteral(CmdLiteral(syntax)),
SyntaxKind::NONSTANDARD_IDENTIFIER => {
Expr::NonstandardIdentifier(NonstandardIdentifier(syntax))
}
SyntaxKind::INTERPOLATION => Expr::Interpolation(Interpolation(syntax)),
SyntaxKind::NAME => Expr::Name(Name(syntax)),
SyntaxKind::BINARY_EXPR => Expr::BinaryExpr(BinaryExpr(syntax)),
SyntaxKind::UNARY_EXPR => Expr::UnaryExpr(UnaryExpr(syntax)),
SyntaxKind::PAREN_EXPR => Expr::ParenExpr(ParenExpr(syntax)),
SyntaxKind::TUPLE_EXPR => Expr::TupleExpr(TupleExpr(syntax)),
SyntaxKind::VECT_EXPR => Expr::VectExpr(VectExpr(syntax)),
SyntaxKind::MATRIX_EXPR => Expr::MatrixExpr(MatrixExpr(syntax)),
SyntaxKind::COMPREHENSION => Expr::Comprehension(Comprehension(syntax)),
SyntaxKind::GENERATOR => Expr::Generator(Generator(syntax)),
SyntaxKind::CALL_EXPR => Expr::CallExpr(CallExpr(syntax)),
SyntaxKind::INDEX_EXPR => Expr::IndexExpr(IndexExpr(syntax)),
SyntaxKind::DOT_CALL_EXPR => Expr::DotCallExpr(DotCallExpr(syntax)),
SyntaxKind::CURLY_EXPR => Expr::CurlyExpr(CurlyExpr(syntax)),
SyntaxKind::BRACES => Expr::Braces(Braces(syntax)),
SyntaxKind::ASSIGNMENT_EXPR => Expr::AssignmentExpr(AssignmentExpr(syntax)),
SyntaxKind::ARROW_EXPR => Expr::ArrowExpr(ArrowExpr(syntax)),
SyntaxKind::TERNARY_EXPR => Expr::TernaryExpr(TernaryExpr(syntax)),
SyntaxKind::IF_EXPR => Expr::IfExpr(IfExpr(syntax)),
SyntaxKind::BEGIN_EXPR => Expr::BeginExpr(BeginExpr(syntax)),
SyntaxKind::QUOTE_EXPR => Expr::QuoteExpr(QuoteExpr(syntax)),
SyntaxKind::WHILE_EXPR => Expr::WhileExpr(WhileExpr(syntax)),
SyntaxKind::FOR_EXPR => Expr::ForExpr(ForExpr(syntax)),
SyntaxKind::LET_EXPR => Expr::LetExpr(LetExpr(syntax)),
SyntaxKind::TRY_EXPR => Expr::TryExpr(TryExpr(syntax)),
SyntaxKind::DO_EXPR => Expr::DoExpr(DoExpr(syntax)),
SyntaxKind::FUNCTION_DEF => Expr::FunctionDef(FunctionDef(syntax)),
SyntaxKind::MACRO_CALL => Expr::MacroCall(MacroCall(syntax)),
SyntaxKind::SPLAT_EXPR => Expr::SplatExpr(SplatExpr(syntax)),
SyntaxKind::WHERE_EXPR => Expr::WhereExpr(WhereExpr(syntax)),
SyntaxKind::TYPE_ANNOTATION => Expr::TypeAnnotation(TypeAnnotation(syntax)),
SyntaxKind::RETURN_EXPR => Expr::ReturnExpr(ReturnExpr(syntax)),
SyntaxKind::BREAK_EXPR => Expr::BreakExpr(BreakExpr(syntax)),
SyntaxKind::CONTINUE_EXPR => Expr::ContinueExpr(ContinueExpr(syntax)),
kind if is_expr_kind(kind) => Expr::Other(syntax),
_ => return None,
};
Some(expr)
}
fn syntax(&self) -> &SyntaxNode {
match self {
Expr::Literal(it) => it.syntax(),
Expr::StringLiteral(it) => it.syntax(),
Expr::CmdLiteral(it) => it.syntax(),
Expr::NonstandardIdentifier(it) => it.syntax(),
Expr::Interpolation(it) => it.syntax(),
Expr::Name(it) => it.syntax(),
Expr::BinaryExpr(it) => it.syntax(),
Expr::UnaryExpr(it) => it.syntax(),
Expr::ParenExpr(it) => it.syntax(),
Expr::TupleExpr(it) => it.syntax(),
Expr::VectExpr(it) => it.syntax(),
Expr::MatrixExpr(it) => it.syntax(),
Expr::Comprehension(it) => it.syntax(),
Expr::Generator(it) => it.syntax(),
Expr::CallExpr(it) => it.syntax(),
Expr::IndexExpr(it) => it.syntax(),
Expr::DotCallExpr(it) => it.syntax(),
Expr::CurlyExpr(it) => it.syntax(),
Expr::Braces(it) => it.syntax(),
Expr::AssignmentExpr(it) => it.syntax(),
Expr::ArrowExpr(it) => it.syntax(),
Expr::TernaryExpr(it) => it.syntax(),
Expr::IfExpr(it) => it.syntax(),
Expr::BeginExpr(it) => it.syntax(),
Expr::QuoteExpr(it) => it.syntax(),
Expr::WhileExpr(it) => it.syntax(),
Expr::ForExpr(it) => it.syntax(),
Expr::LetExpr(it) => it.syntax(),
Expr::TryExpr(it) => it.syntax(),
Expr::DoExpr(it) => it.syntax(),
Expr::FunctionDef(it) => it.syntax(),
Expr::MacroCall(it) => it.syntax(),
Expr::SplatExpr(it) => it.syntax(),
Expr::WhereExpr(it) => it.syntax(),
Expr::TypeAnnotation(it) => it.syntax(),
Expr::ReturnExpr(it) => it.syntax(),
Expr::BreakExpr(it) => it.syntax(),
Expr::ContinueExpr(it) => it.syntax(),
Expr::Other(it) => it,
}
}
}
impl Name {
pub fn ident(&self) -> Option<Ident> {
child_token(&self.0)
}
}
impl Literal {
pub fn bool_token(&self) -> Option<SyntaxToken> {
support::token(&self.0, SyntaxKind::TRUE_KW)
.or_else(|| support::token(&self.0, SyntaxKind::FALSE_KW))
}
}
impl NonstandardIdentifier {
pub fn content(&self) -> Option<SyntaxToken> {
support::token(&self.0, SyntaxKind::STRING_CONTENT)
}
}
impl StringLiteral {
pub fn prefix(&self) -> Option<SyntaxToken> {
support::token(&self.0, SyntaxKind::STRING_PREFIX)
}
pub fn suffix(&self) -> Option<SyntaxToken> {
support::token(&self.0, SyntaxKind::STRING_SUFFIX)
}
pub fn interpolations(&self) -> AstChildren<Interpolation> {
support::children(&self.0)
}
pub fn content_tokens(&self) -> impl Iterator<Item = SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|el| el.into_token())
.filter(|t| t.kind() == SyntaxKind::STRING_CONTENT)
}
}
impl CmdLiteral {
pub fn prefix(&self) -> Option<SyntaxToken> {
support::token(&self.0, SyntaxKind::STRING_PREFIX)
}
pub fn suffix(&self) -> Option<SyntaxToken> {
support::token(&self.0, SyntaxKind::STRING_SUFFIX)
}
pub fn interpolations(&self) -> AstChildren<Interpolation> {
support::children(&self.0)
}
}
impl Interpolation {
pub fn ident(&self) -> Option<Ident> {
child_token(&self.0)
}
pub fn expr(&self) -> Option<Expr> {
support::child(&self.0)
}
}
impl BinaryExpr {
pub fn lhs(&self) -> Option<Expr> {
support::child(&self.0)
}
pub fn rhs(&self) -> Option<Expr> {
support::children(&self.0).nth(1)
}
pub fn op(&self) -> Option<Operator> {
child_token(&self.0)
}
}
impl AssignmentExpr {
pub fn lhs(&self) -> Option<Expr> {
support::child(&self.0)
}
pub fn rhs(&self) -> Option<Expr> {
support::children(&self.0).nth(1)
}
pub fn op(&self) -> Option<Operator> {
child_token(&self.0)
}
}
impl ArrowExpr {
pub fn lhs(&self) -> Option<Expr> {
support::child(&self.0)
}
pub fn rhs(&self) -> Option<Expr> {
support::children(&self.0).nth(1)
}
}
impl UnaryExpr {
pub fn op(&self) -> Option<Operator> {
child_token(&self.0)
}
pub fn operand(&self) -> Option<Expr> {
support::child(&self.0)
}
}
impl SplatExpr {
pub fn expr(&self) -> Option<Expr> {
support::child(&self.0)
}
}
impl ParenExpr {
pub fn expr(&self) -> Option<Expr> {
support::child(&self.0)
}
}
impl CallExpr {
pub fn callee(&self) -> Option<Expr> {
support::child(&self.0)
}
pub fn callee_operator(&self) -> Option<Operator> {
for el in self.0.children_with_tokens() {
match el {
rowan::NodeOrToken::Token(token) => {
if let Some(op) = Operator::cast(token) {
return Some(op);
}
}
rowan::NodeOrToken::Node(node) => {
return match node.kind() {
SyntaxKind::PAREN_EXPR => paren_operator(&node),
SyntaxKind::BINARY_EXPR => node
.children()
.last()
.filter(|last| last.kind() == SyntaxKind::QUOTE_SYM)
.and_then(|quote| quote_sym_operator("e)),
_ => None,
};
}
}
}
None
}
}
fn paren_operator(paren: &SyntaxNode) -> Option<Operator> {
paren
.children_with_tokens()
.filter_map(|el| el.into_token())
.find_map(Operator::cast)
}
fn quote_sym_operator(quote: &SyntaxNode) -> Option<Operator> {
let mut seen_quote_colon = false;
for el in quote.children_with_tokens() {
match el {
rowan::NodeOrToken::Token(token) => {
if !seen_quote_colon && token.kind() == SyntaxKind::COLON {
seen_quote_colon = true;
} else if let Some(op) = Operator::cast(token) {
return Some(op);
}
}
rowan::NodeOrToken::Node(node) if node.kind() == SyntaxKind::PAREN_EXPR => {
return paren_operator(&node);
}
_ => {}
}
}
None
}
impl IndexExpr {
pub fn base(&self) -> Option<Expr> {
support::child(&self.0)
}
}
impl DotCallExpr {
pub fn callee(&self) -> Option<Expr> {
support::child(&self.0)
}
}
impl CurlyExpr {
pub fn base(&self) -> Option<Expr> {
support::child(&self.0)
}
}
impl ArgList {
pub fn args(&self) -> AstChildren<Arg> {
support::children(&self.0)
}
pub fn keyword_args(&self) -> AstChildren<KeywordArg> {
support::children(&self.0)
}
}
impl Arg {
pub fn expr(&self) -> Option<Expr> {
support::child(&self.0)
}
}
impl KeywordArg {
pub fn name(&self) -> Option<Name> {
support::child(&self.0)
}
pub fn value(&self) -> Option<Expr> {
support::children(&self.0).nth(1)
}
}
impl MacroCall {
pub fn name(&self) -> Option<MacroName> {
support::child(&self.0)
}
}
impl MacroName {
pub fn macro_token(&self) -> Option<SyntaxToken> {
self.0
.children_with_tokens()
.filter_map(|e| e.into_token())
.filter(|t| matches!(t.kind(), SyntaxKind::IDENT | SyntaxKind::DOT))
.last()
}
}
impl TypeAnnotation {
pub fn op(&self) -> Option<SyntaxToken> {
support::token(&self.0, SyntaxKind::COLON_COLON)
}
}
impl IfExpr {
pub fn then_body(&self) -> Option<Block> {
support::child(&self.0)
}
pub fn elseif_clauses(&self) -> AstChildren<ElseifClause> {
support::children(&self.0)
}
pub fn else_clause(&self) -> Option<ElseClause> {
support::child(&self.0)
}
}
impl Condition {
pub fn expr(&self) -> Option<Expr> {
match support::child::<Expr>(&self.0)? {
Expr::ParenExpr(paren) => paren.expr(),
other => Some(other),
}
}
}
impl FunctionDef {
pub fn signature(&self) -> Option<Signature> {
support::child(&self.0)
}
}
impl Signature {
pub fn expr(&self) -> Option<Expr> {
support::child(&self.0)
}
}
impl ModuleDef {
pub fn name(&self) -> Option<Name> {
support::child::<Signature>(&self.0).and_then(|sig| support::child(&sig.0))
}
}
impl ForBinding {
pub fn pattern(&self) -> Option<Expr> {
support::child(&self.0)
}
pub fn iterable(&self) -> Option<Expr> {
support::children(&self.0).nth(1)
}
}
impl CatchClause {
pub fn variable(&self) -> Option<Name> {
support::child(&self.0)
}
}
impl DoExpr {
pub fn params(&self) -> Option<DoParams> {
support::child(&self.0)
}
}
impl Root {
pub fn items(&self) -> AstChildren<Expr> {
support::children(&self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::tokens::AstToken;
use crate::ast::traits::{HasArgList, HasBody, HasCondition};
use crate::parser::parse;
fn find<T: AstNode<Language = JuliaLanguage>>(src: &str) -> T {
parse(src)
.cst
.descendants()
.find_map(T::cast)
.expect("node of the requested kind present")
}
fn expr_text(expr: Option<Expr>) -> String {
expr.expect("expression present")
.syntax()
.text()
.to_string()
}
#[test]
fn binary_expr_operands_and_operator() {
let bin: BinaryExpr = find("a + b\n");
assert_eq!(expr_text(bin.lhs()), "a");
assert_eq!(expr_text(bin.rhs()), "b");
assert_eq!(bin.op().unwrap().text(), "+");
}
#[test]
fn assignment_operands_and_operator() {
let assign: AssignmentExpr = find("x = f(1)\n");
assert_eq!(expr_text(assign.lhs()), "x");
assert!(matches!(assign.rhs(), Some(Expr::CallExpr(_))));
assert_eq!(assign.op().unwrap().text(), "=");
}
#[test]
fn call_callee_and_args() {
let call: CallExpr = find("g(a, b)\n");
assert_eq!(expr_text(call.callee()), "g");
let args = call.arg_list().expect("arg list");
let texts: Vec<String> = args
.args()
.map(|a| a.expr().unwrap().syntax().text().to_string())
.collect();
assert_eq!(texts, ["a", "b"]);
}
#[test]
fn call_callee_operator_shapes() {
let call: CallExpr = find("!=(a, b)\n");
assert_eq!(call.callee_operator().unwrap().text(), "!=");
let call: CallExpr = find("(!=)(a, b)\n");
assert_eq!(call.callee_operator().unwrap().text(), "!=");
let call: CallExpr = find("Base.:!=(a, b)\n");
assert_eq!(call.callee_operator().unwrap().text(), "!=");
let call: CallExpr = find("Base.:(==)(a, b)\n");
assert_eq!(call.callee_operator().unwrap().text(), "==");
let call: CallExpr = find("g(a, b)\n");
assert!(call.callee_operator().is_none());
let call: CallExpr = find("Base.g(a, b)\n");
assert!(call.callee_operator().is_none());
}
#[test]
fn if_condition_body_and_else() {
let if_expr: IfExpr = find("if x\n 1\nelse\n 2\nend\n");
let cond = if_expr.condition().expect("condition");
assert_eq!(expr_text(cond.expr()), "x");
assert!(if_expr.then_body().is_some());
assert!(if_expr.else_clause().and_then(|e| e.body()).is_some());
}
#[test]
fn literal_bool_token() {
assert_eq!(
find::<Literal>("true\n").bool_token().unwrap().text(),
"true"
);
assert_eq!(
find::<Literal>("false\n").bool_token().unwrap().text(),
"false"
);
assert!(find::<Literal>("1\n").bool_token().is_none());
}
#[test]
fn condition_unwraps_one_paren_layer() {
let cond: Condition = find("if (x = 1)\n 1\nend\n");
assert!(matches!(cond.expr(), Some(Expr::AssignmentExpr(_))));
}
#[test]
fn function_signature_and_body() {
let func: FunctionDef = find("function f(x)\n x\nend\n");
assert_eq!(expr_text(func.signature().and_then(|s| s.expr())), "f(x)");
assert!(func.body().is_some());
}
#[test]
fn expr_dispatches_known_kinds() {
assert!(matches!(
Expr::cast(find::<CallExpr>("f()\n").syntax().clone()),
Some(Expr::CallExpr(_))
));
assert!(matches!(
Expr::cast(find::<Name>("x\n").syntax().clone()),
Some(Expr::Name(_))
));
}
#[test]
fn expr_falls_back_to_other_for_unwrapped_kinds() {
let cmp = parse("a < b < c\n")
.cst
.descendants()
.find(|n| n.kind() == SyntaxKind::COMPARISON_EXPR)
.expect("comparison expr");
assert!(matches!(Expr::cast(cmp), Some(Expr::Other(_))));
}
#[test]
fn non_expression_nodes_do_not_cast_to_expr() {
let sig = find::<FunctionDef>("function f()\nend\n")
.signature()
.unwrap();
assert!(Expr::cast(sig.syntax().clone()).is_none());
}
#[test]
fn module_def_name_and_body() {
let module: ModuleDef = find("module M\nx = 1\nend\n");
assert_eq!(module.name().unwrap().syntax().text(), "M");
assert!(module.body().is_some());
let bare: ModuleDef = find("baremodule B\nend\n");
assert_eq!(bare.name().unwrap().syntax().text(), "B");
}
#[test]
fn missing_children_yield_none() {
let do_expr: DoExpr = find("f() do\n 1\nend\n");
assert!(do_expr.params().is_none());
}
}