Skip to content

Commit

Permalink
docs(semantic): add doc comments for SymbolTester and SemanticTester (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
DonIsaac committed Jul 24, 2024
1 parent 4f5a7cb commit 871b3d6
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 1 deletion.
3 changes: 3 additions & 0 deletions crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ impl<'a> SemanticBuilder<'a> {
self
}

/// Enable or disable building a [`ControlFlowGraph`].
///
/// [`ControlFlowGraph`]: oxc_cfg::ControlFlowGraph
#[must_use]
pub fn with_cfg(mut self, cfg: bool) -> Self {
self.cfg = if cfg { Some(ControlFlowGraphBuilder::default()) } else { None };
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_semantic/tests/integration/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ fn test_types_simple() {
.has_some_symbol("T")
.contains_flags(SymbolFlags::TypeParameter)
.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();

SemanticTester::ts("function foo<T>(a: T): void {}")
Expand Down
27 changes: 27 additions & 0 deletions crates/oxc_semantic/tests/integration/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ pub use symbol_tester::SymbolTester;

#[must_use]
pub struct SemanticTester<'a> {
/// Memory arena that AST and [`Semantic`] will store data in.
allocator: Allocator,
/// Source type of the test case.
source_type: SourceType,
/// The source text of the test case.
source_text: &'a str,
/// Build a [`ControlFlowGraph`]?
///
/// [`ControlFlowGraph`]: oxc_cfg::ControlFlowGraph
cfg: bool,
/// Expect semantic analysis to produce errors.
///
Expand Down Expand Up @@ -47,6 +53,10 @@ impl<'a> SemanticTester<'a> {
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 {
Self {
allocator: Allocator::default(),
Expand All @@ -64,20 +74,27 @@ impl<'a> SemanticTester<'a> {
}

/// Mark the [`SourceType`] as JSX
///
/// If the source is currently TypeScript, it will become TSX.
pub fn with_jsx(mut self, yes: bool) -> Self {
self.source_type = self.source_type.with_jsx(yes);
self
}

/// Mark the [`SourceType`] as an ESM module.
pub fn with_module(mut self, yes: bool) -> Self {
self.source_type = self.source_type.with_module(yes);
self
}

/// Enable or disable building a [`ControlFlowGraph`].
///
/// [`ControlFlowGraph`]: oxc_cfg::ControlFlowGraph
pub fn with_cfg(mut self, yes: bool) -> Self {
self.cfg = yes;
self
}

/// The program being tested is expected to produce errors during semantic analysis.
///
/// By default, programs are expected to be error-free.
Expand Down Expand Up @@ -201,6 +218,16 @@ impl<'a> SemanticTester<'a> {
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) {
let SemanticBuilderReturn { errors, .. } = self.build_with_errors();
assert!(
Expand Down
45 changes: 45 additions & 0 deletions crates/oxc_semantic/tests/integration/util/symbol_tester.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,30 @@ use oxc_span::CompactStr;

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]
pub struct SymbolTester<'a> {
parent: &'a SemanticTester<'a>,
Expand Down Expand Up @@ -131,18 +155,27 @@ impl<'a> SymbolTester<'a> {
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 {
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 {
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 {
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
where
F: FnMut(&Reference) -> bool,
Expand Down Expand Up @@ -170,6 +203,12 @@ impl<'a> SymbolTester<'a> {
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)]
pub fn is_exported(mut self) -> Self {
self.test_result = match self.test_result {
Expand All @@ -188,6 +227,12 @@ impl<'a> SymbolTester<'a> {
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)]
pub fn is_not_exported(mut self) -> Self {
self.test_result = match self.test_result {
Expand Down

0 comments on commit 871b3d6

Please sign in to comment.