use crate::ast::{AstNode, AstToken, Expr, HasArgList, MacroCall};
use crate::linter::rules::RuleContext;
use crate::syntax::SyntaxNode;
pub(crate) struct TestInvocation {
pub(crate) expression: Expr,
}
pub(crate) fn test_invocation(call: &MacroCall, ctx: &RuleContext<'_>) -> Option<TestInvocation> {
let root = call.name()?.ident_tokens().next()?;
let scope = ctx
.model
.idents()
.iter()
.find(|ident| ident.range == root.syntax().text_range())?
.scope;
if !ctx.model.is_test_macro(call, scope, "test") {
return None;
}
Some(TestInvocation {
expression: primary_expression(call)?,
})
}
pub(crate) fn is_direct_test_expression(node: &SyntaxNode, ctx: &RuleContext<'_>) -> bool {
node.ancestors().skip(1).any(|ancestor| {
MacroCall::cast(ancestor)
.and_then(|call| test_invocation(&call, ctx))
.is_some_and(|invocation| {
invocation.expression.syntax().text_range() == node.text_range()
})
})
}
fn primary_expression(call: &MacroCall) -> Option<Expr> {
if let Some(args) = call.arg_list() {
let mut positional = args.args().filter_map(|arg| arg.expr());
let expression = positional.next()?;
return positional.next().is_none().then_some(expression);
}
let mut children = call.syntax().children().filter_map(Expr::cast);
let expression = children.next()?;
children
.all(|arg| matches!(arg, Expr::AssignmentExpr(_)))
.then_some(expression)
}