Skip to content

Commit

Permalink
test(semantic): add scope tests for IIFEs
Browse files Browse the repository at this point in the history
  • Loading branch information
DonIsaac authored and Dunqing committed Jul 25, 2024
1 parent fd363d1 commit 6f83fc2
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 7 deletions.
57 changes: 56 additions & 1 deletion crates/oxc_semantic/tests/integration/scopes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use oxc_semantic::ScopeFlags;
use oxc_ast::AstKind;
use oxc_semantic::{ScopeFlags, SymbolFlags};

use crate::util::{Expect, SemanticTester};

Expand Down Expand Up @@ -106,6 +107,60 @@ fn test_switch_case() {
.test();
}

#[allow(clippy::disallowed_names)]
#[test]
fn test_function_scopes() {
let test = SemanticTester::ts("function foo() { return foo() }");
let foo_id = test
.has_root_symbol("foo")
.contains_flags(SymbolFlags::Function)
.contains_flags(SymbolFlags::BlockScopedVariable)
.has_number_of_reads(1)
.test();

let semantic = test.build();
let root_id = semantic.scopes().root_scope_id();
let foo_scope_id = semantic.symbols().get_scope_id(foo_id);
assert_eq!(foo_scope_id, root_id, "Expected fn foo to be in the root scope.");

let foo_node = semantic.nodes().get_node(semantic.symbols().get_declaration(foo_id));
let AstKind::Function(foo) = foo_node.kind() else {
panic!("Expected foo's declaration node to be a FunctionDeclaration");
};
assert!(foo.is_declaration(), "Expected foo's declaration node to be a FunctionDeclaration");
assert_eq!(foo_node.scope_id(), root_id, "Expected fn foo to be in the root scope.");
assert_ne!(
foo.scope_id.get().unwrap(),
root_id,
"function bodies should not be the root scope"
);

let binding_id = semantic
.scopes()
.get_binding(root_id, "foo")
.expect("Expected to find a binding for fn foo");
assert_eq!(binding_id, foo_id);

// =========================================================================

let test = SemanticTester::ts("(function foo() { return foo() })()");
let foo_id = test
.has_some_symbol("foo")
.contains_flags(SymbolFlags::Function)
.does_not_contain_flags(SymbolFlags::BlockScopedVariable)
.has_number_of_reads(1)
.test();

let semantic = test.build();
let root_id = semantic.scopes().root_scope_id();

let foo_node = semantic.nodes().get_node(semantic.symbols().get_declaration(foo_id));
let foo_scope_id = semantic.symbols().get_scope_id(foo_id);
assert_eq!(foo_node.scope_id(), root_id);
// FIXME: These should be equal
assert_ne!(foo_node.scope_id(), foo_scope_id);
}

#[test]
fn test_function_parameters() {
let tester = SemanticTester::js(
Expand Down
9 changes: 9 additions & 0 deletions crates/oxc_semantic/tests/integration/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ fn test_function_expressions() {
.test();
}

#[test]
fn test_function_iifes() {
SemanticTester::ts("(function foo() {})(); foo()")
.has_some_symbol("foo")
.contains_flags(SymbolFlags::Function)
.has_number_of_references(0)
.test();
}

#[test]
fn test_var_simple() {
SemanticTester::js("let x; { let y; }")
Expand Down
35 changes: 29 additions & 6 deletions crates/oxc_semantic/tests/integration/util/symbol_tester.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,24 @@ impl<'a> SymbolTester<'a> {
self
}

pub fn does_not_contain_flags(mut self, flags: SymbolFlags) -> Self {
self.test_result = match self.test_result {
Ok(symbol_id) => {
let found_flags = self.semantic.symbols().get_flag(symbol_id);
if found_flags.contains(flags) {
Err(OxcDiagnostic::error(format!(
"Expected {} to not contain flags {:?}, but it had {:?}",
self.target_symbol_name, flags, found_flags
)))
} else {
Ok(symbol_id)
}
}
err => err,
};
self
}

/// Check that this symbol has a certain number of read [`Reference`]s
///
/// References that are both read and write are counted.
Expand Down Expand Up @@ -292,11 +310,16 @@ impl<'a> SymbolTester<'a> {
}

/// Complete the test case. Will panic if any of the previously applied
/// assertions failed.
pub fn test(self) {
let res: Result<_, _> = self.into();
/// assertions failed. If all assertions pass, this function will return
/// the [`SymbolId`] of the target symbol.
///
/// # Panics
/// - If any of the previously applied assertions failed.
/// - If the symbol could not be found in the semantic analysis results.
pub fn test(self) -> SymbolId {
let res: Result<SymbolId, _> = self.into();

res.unwrap();
res.unwrap()
}
}

Expand Down Expand Up @@ -344,9 +367,9 @@ impl<'a> Expect<(Rc<Semantic<'a>>, SymbolId), Result<(), OxcDiagnostic>> for Sym
}
}

impl<'a> From<SymbolTester<'a>> for Result<(), Error> {
impl<'a> From<SymbolTester<'a>> for Result<SymbolId, Error> {
fn from(val: SymbolTester<'a>) -> Self {
let source_code = val.parent.source_text.to_string();
val.test_result.map(|_| {}).map_err(|e| e.with_source_code(source_code))
val.test_result.map_err(|e| e.with_source_code(source_code))
}
}

0 comments on commit 6f83fc2

Please sign in to comment.