use super::rust;
use super::tree::{AstNode, ParsedFile};
use crate::diagnostic::Language;
use std::collections::HashSet;
impl ParsedFile {
#[must_use]
pub(super) fn is_in_rust_inline_test(&self, offset: usize) -> bool {
self.lang() == Language::Rust
&& rust::collect_inline_test_ranges(self)
.iter()
.any(|range| range.contains(&offset))
}
}
impl AstNode<'_> {
#[must_use]
pub fn is_in_rust_inline_test(&self) -> bool {
self.file.is_in_rust_inline_test(self.span.start)
}
}
pub struct TestAssertionCount<'a> {
pub name_node: AstNode<'a>,
pub name: String,
pub count: usize,
}
pub struct TestLogicConstruct<'a> {
pub node: AstNode<'a>,
pub test_name: String,
pub construct: &'static str,
}
pub struct MultiPhaseTest<'a> {
pub name_node: AstNode<'a>,
pub name: String,
}
pub struct AssertionCheck<'a> {
pub node: AstNode<'a>,
pub callee: String,
pub has_top_level_logical_and: bool,
pub has_boolean_literal_comparison: bool,
pub has_composite_collection_comparison: bool,
}
pub struct TestFunctionSetup<'a> {
pub name_node: AstNode<'a>,
pub name: String,
pub leading_setup_statements: Vec<String>,
}
pub struct TestToTestCall<'a> {
pub node: AstNode<'a>,
pub caller: String,
pub callee: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum TestStatementRole {
Assertion,
InspectionOrGuard,
Action,
}
#[must_use]
pub fn collect_test_function_assertion_counts(file: &ParsedFile) -> Vec<TestAssertionCount<'_>> {
dispatch_lang!(file.lang(), collect_test_function_assertion_counts(file))
}
#[must_use]
pub fn collect_test_logic_constructs(file: &ParsedFile) -> Vec<TestLogicConstruct<'_>> {
dispatch_lang!(file.lang(), collect_test_logic_constructs(file))
}
#[must_use]
pub fn collect_multi_phase_test_functions(file: &ParsedFile) -> Vec<MultiPhaseTest<'_>> {
dispatch_lang!(file.lang(), collect_multi_phase_test_functions(file))
}
#[must_use]
pub fn collect_assertion_checks(file: &ParsedFile) -> Vec<AssertionCheck<'_>> {
dispatch_lang!(file.lang(), collect_assertion_checks(file))
}
#[must_use]
pub fn collect_test_setup_scopes(file: &ParsedFile) -> Vec<Vec<TestFunctionSetup<'_>>> {
dispatch_lang!(file.lang(), collect_test_setup_scopes(file))
}
#[must_use]
pub fn collect_test_to_test_calls(file: &ParsedFile) -> Vec<TestToTestCall<'_>> {
dispatch_lang!(file.lang(), collect_test_to_test_calls(file))
}
pub(super) fn resolve_assertion_helpers<'a, T: 'a>(
candidates: &'a [T],
name_of: impl Fn(&'a T) -> &'a str,
is_helper: impl Fn(&'a T, &HashSet<String>) -> bool,
) -> HashSet<String> {
let mut helpers = HashSet::new();
loop {
let mut added = false;
for candidate in candidates {
let name = name_of(candidate);
if !helpers.contains(name) && is_helper(candidate, &helpers) {
helpers.insert(name.to_owned());
added = true;
}
}
if !added {
break;
}
}
helpers
}
pub(super) fn has_multi_phase_interleaving(
roles: impl IntoIterator<Item = TestStatementRole>,
) -> bool {
let mut seen_assertion = false;
let mut seen_action_after_assertion = false;
for role in roles {
match role {
TestStatementRole::InspectionOrGuard => {}
TestStatementRole::Assertion => {
if seen_action_after_assertion {
return true;
}
seen_assertion = true;
}
TestStatementRole::Action => {
if seen_assertion {
seen_action_after_assertion = true;
}
}
}
}
false
}
pub(super) fn normalize_statement_text(raw: &str) -> String {
raw.lines().map(str::trim).collect::<Vec<_>>().join("\n")
}
#[must_use]
pub(super) fn is_assertion_function_name(name: &str) -> bool {
name.starts_with("assert_") || name.starts_with("_assert_")
}