docs(semantic): add doc comments for SymbolTester and SemanticTester (#4433)

This commit is contained in:
DonIsaac 2024-07-24 16:58:13 +00:00
parent 4f5a7cbf3c
commit 871b3d6135
4 changed files with 76 additions and 1 deletions

View file

@ -147,6 +147,9 @@ impl<'a> SemanticBuilder<'a> {
self self
} }
/// Enable or disable building a [`ControlFlowGraph`].
///
/// [`ControlFlowGraph`]: oxc_cfg::ControlFlowGraph
#[must_use] #[must_use]
pub fn with_cfg(mut self, cfg: bool) -> Self { pub fn with_cfg(mut self, cfg: bool) -> Self {
self.cfg = if cfg { Some(ControlFlowGraphBuilder::default()) } else { None }; self.cfg = if cfg { Some(ControlFlowGraphBuilder::default()) } else { None };

View file

@ -113,7 +113,7 @@ fn test_types_simple() {
.has_some_symbol("T") .has_some_symbol("T")
.contains_flags(SymbolFlags::TypeParameter) .contains_flags(SymbolFlags::TypeParameter)
.has_number_of_references(1) .has_number_of_references(1)
.has_number_of_references_where(1, oxc_semantic::Reference::is_type) .has_number_of_references_where(1, Reference::is_type)
.test(); .test();
SemanticTester::ts("function foo<T>(a: T): void {}") SemanticTester::ts("function foo<T>(a: T): void {}")

View file

@ -15,9 +15,15 @@ pub use symbol_tester::SymbolTester;
#[must_use] #[must_use]
pub struct SemanticTester<'a> { pub struct SemanticTester<'a> {
/// Memory arena that AST and [`Semantic`] will store data in.
allocator: Allocator, allocator: Allocator,
/// Source type of the test case.
source_type: SourceType, source_type: SourceType,
/// The source text of the test case.
source_text: &'a str, source_text: &'a str,
/// Build a [`ControlFlowGraph`]?
///
/// [`ControlFlowGraph`]: oxc_cfg::ControlFlowGraph
cfg: bool, cfg: bool,
/// Expect semantic analysis to produce errors. /// Expect semantic analysis to produce errors.
/// ///
@ -47,6 +53,10 @@ impl<'a> SemanticTester<'a> {
Self::new(source_text, SourceType::default().with_module(true)) Self::new(source_text, SourceType::default().with_module(true))
} }
/// Create a new tester for some source text.
///
/// You may find one of the other factory methods, like
/// [`SemanticTester::ts`] or [`SemanticTester::js`], more convenient.
pub fn new(source_text: &'a str, source_type: SourceType) -> Self { pub fn new(source_text: &'a str, source_type: SourceType) -> Self {
Self { Self {
allocator: Allocator::default(), allocator: Allocator::default(),
@ -64,20 +74,27 @@ impl<'a> SemanticTester<'a> {
} }
/// Mark the [`SourceType`] as JSX /// Mark the [`SourceType`] as JSX
///
/// If the source is currently TypeScript, it will become TSX.
pub fn with_jsx(mut self, yes: bool) -> Self { pub fn with_jsx(mut self, yes: bool) -> Self {
self.source_type = self.source_type.with_jsx(yes); self.source_type = self.source_type.with_jsx(yes);
self self
} }
/// Mark the [`SourceType`] as an ESM module.
pub fn with_module(mut self, yes: bool) -> Self { pub fn with_module(mut self, yes: bool) -> Self {
self.source_type = self.source_type.with_module(yes); self.source_type = self.source_type.with_module(yes);
self self
} }
/// Enable or disable building a [`ControlFlowGraph`].
///
/// [`ControlFlowGraph`]: oxc_cfg::ControlFlowGraph
pub fn with_cfg(mut self, yes: bool) -> Self { pub fn with_cfg(mut self, yes: bool) -> Self {
self.cfg = yes; self.cfg = yes;
self self
} }
/// The program being tested is expected to produce errors during semantic analysis. /// The program being tested is expected to produce errors during semantic analysis.
/// ///
/// By default, programs are expected to be error-free. /// By default, programs are expected to be error-free.
@ -201,6 +218,16 @@ impl<'a> SemanticTester<'a> {
ClassTester::has_class(self.build(), name) ClassTester::has_class(self.build(), name)
} }
/// Check if semantic analysis produces an error matching `message`.
///
/// Matching is done using `.contains()`.
///
/// ## Fails
/// - If [`parsing`] produces errors
/// - If [`SemanticBuilder`] finishes without producing any errors.
/// - If [`SemanticBuilder`] finishes with errors, but none of them match `message`
///
/// [`parsing`]: oxc_parser::Parser::parse
pub fn has_error(&self, message: &str) { pub fn has_error(&self, message: &str) {
let SemanticBuilderReturn { errors, .. } = self.build_with_errors(); let SemanticBuilderReturn { errors, .. } = self.build_with_errors();
assert!( assert!(

View file

@ -6,6 +6,30 @@ use oxc_span::CompactStr;
use super::{Expect, SemanticTester}; use super::{Expect, SemanticTester};
/// Test a symbol in the [`Semantic`] analysis results.
///
/// To use this, chain together assertions about the symbol, such as
/// [`SymbolTester::contains_flags`], [`SymbolTester::has_number_of_reads`],
/// etc., then finish the chain off with a call to [`SymbolTester::test`].
///
/// You will never create this struct manually. Instead, use one of
/// [`SemanticTester`]'s factories, such as [`SemanticTester::has_root_symbol`].
///
/// # Example
/// ```
/// use oxc_semantic::{SymbolFlags, Semantic};
/// use super::SemanticTester;
///
/// #[test]
/// fn my_test() {
/// SemanticTester::js("let x = 0; let foo = (0, x++)")
/// .has_some_symbol("x") // find a symbol named "x" at any scope
/// .contains_flags(SymbolFlags::Variable) // check that it's a variable
/// .has_number_of_reads(1) // check read references
/// .has_number_of_writes(1) // check write references
/// .test(); // finish the test. Will panic if any assertions failed.
/// }
///```
#[must_use] #[must_use]
pub struct SymbolTester<'a> { pub struct SymbolTester<'a> {
parent: &'a SemanticTester<'a>, parent: &'a SemanticTester<'a>,
@ -131,18 +155,27 @@ impl<'a> SymbolTester<'a> {
self self
} }
/// Check that this symbol has a certain number of read [`Reference`]s
///
/// References that are both read and write are counted.
pub fn has_number_of_reads(self, ref_count: usize) -> Self { pub fn has_number_of_reads(self, ref_count: usize) -> Self {
self.has_number_of_references_where(ref_count, Reference::is_read) self.has_number_of_references_where(ref_count, Reference::is_read)
} }
/// Check that this symbol has a certain number of write [`Reference`]s.
///
/// References that are both read and write are counted.
pub fn has_number_of_writes(self, ref_count: usize) -> Self { pub fn has_number_of_writes(self, ref_count: usize) -> Self {
self.has_number_of_references_where(ref_count, Reference::is_write) self.has_number_of_references_where(ref_count, Reference::is_write)
} }
/// Check that this symbol has a certain number of [`Reference`]s of any kind.
pub fn has_number_of_references(self, ref_count: usize) -> Self { pub fn has_number_of_references(self, ref_count: usize) -> Self {
self.has_number_of_references_where(ref_count, |_| true) self.has_number_of_references_where(ref_count, |_| true)
} }
/// Check that this symbol has a certain number of [`Reference`]s that meet
/// some criteria established by a predicate.
pub fn has_number_of_references_where<F>(mut self, ref_count: usize, filter: F) -> Self pub fn has_number_of_references_where<F>(mut self, ref_count: usize, filter: F) -> Self
where where
F: FnMut(&Reference) -> bool, F: FnMut(&Reference) -> bool,
@ -170,6 +203,12 @@ impl<'a> SymbolTester<'a> {
self self
} }
/// Check that this symbol is exported.
///
/// Export status is checked using the symbol's [`SymbolFlags`], not by
/// checking the [`oxc_semantic::ModuleRecord`].
///
/// For the inverse of this assertion, use [`SymbolTester::is_not_exported`].
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub fn is_exported(mut self) -> Self { pub fn is_exported(mut self) -> Self {
self.test_result = match self.test_result { self.test_result = match self.test_result {
@ -188,6 +227,12 @@ impl<'a> SymbolTester<'a> {
self self
} }
/// Check that this symbol is not exported.
///
/// Export status is checked using the symbol's [`SymbolFlags`], not by
/// checking the [`oxc_semantic::ModuleRecord`].
///
/// For the inverse of this assertion, use [`SymbolTester::is_exported`].
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub fn is_not_exported(mut self) -> Self { pub fn is_not_exported(mut self) -> Self {
self.test_result = match self.test_result { self.test_result = match self.test_result {