From 871b3d6135291962625f3c7292041583d842d965 Mon Sep 17 00:00:00 2001 From: DonIsaac <22823424+DonIsaac@users.noreply.github.com> Date: Wed, 24 Jul 2024 16:58:13 +0000 Subject: [PATCH] docs(semantic): add doc comments for SymbolTester and SemanticTester (#4433) --- crates/oxc_semantic/src/builder.rs | 3 ++ .../oxc_semantic/tests/integration/symbols.rs | 2 +- .../tests/integration/util/mod.rs | 27 +++++++++++ .../tests/integration/util/symbol_tester.rs | 45 +++++++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 6a3f9c6a6d368..0fee58d625679 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -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 }; diff --git a/crates/oxc_semantic/tests/integration/symbols.rs b/crates/oxc_semantic/tests/integration/symbols.rs index 14274cc813448..b2c60e81a9529 100644 --- a/crates/oxc_semantic/tests/integration/symbols.rs +++ b/crates/oxc_semantic/tests/integration/symbols.rs @@ -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(a: T): void {}") diff --git a/crates/oxc_semantic/tests/integration/util/mod.rs b/crates/oxc_semantic/tests/integration/util/mod.rs index d92a17d6865ab..38d13714d5597 100644 --- a/crates/oxc_semantic/tests/integration/util/mod.rs +++ b/crates/oxc_semantic/tests/integration/util/mod.rs @@ -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. /// @@ -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(), @@ -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. @@ -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!( diff --git a/crates/oxc_semantic/tests/integration/util/symbol_tester.rs b/crates/oxc_semantic/tests/integration/util/symbol_tester.rs index 0ae267f366b09..adda5f3af1ee7 100644 --- a/crates/oxc_semantic/tests/integration/util/symbol_tester.rs +++ b/crates/oxc_semantic/tests/integration/util/symbol_tester.rs @@ -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>, @@ -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(mut self, ref_count: usize, filter: F) -> Self where F: FnMut(&Reference) -> bool, @@ -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 { @@ -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 {