Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions crates/oxc_linter/src/rules/eslint/no_unused_vars/tests/oxc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,24 @@ fn test_vars_self_use() {
.test_and_snapshot();
}

#[test]
fn test_vars_self_use_js() {
let pass = vec![
// https://github.com/oxc-project/oxc/issues/11215
"export function promisify() { var fn; function fn() {} return fn; }",
];

let fail = vec![
// https://github.com/oxc-project/oxc/issues/11215
"export function promisify() { var fn; function fn() { fn() } }",
];

Tester::new(NoUnusedVars::NAME, NoUnusedVars::PLUGIN, pass, fail)
.change_rule_path_extension("js")
.intentionally_allow_no_fix_tests()
.test();
}

#[test]
fn test_vars_discarded_reads() {
let pass = vec![
Expand Down
17 changes: 14 additions & 3 deletions crates/oxc_linter/src/rules/eslint/no_unused_vars/usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,18 @@ impl<'a> Symbol<'_, 'a> {
fn is_self_call_simple(&self, reference: &Reference) -> bool {
let decl_scope_id = self.scope_id();
let call_scope_id = self.get_ref_scope(reference);
let Some(container_id) = self.declaration().kind().get_container_scope_id() else {
let redeclarations = self.scoping().symbol_redeclarations(self.id());
let container_id = if redeclarations.is_empty() {
self.declaration().kind().get_container_scope_id()
} else {
// Syntax like `var a = 0; function a() { a() }` is legal. We need to
// check the redeclarations to find the one that is a function and use
// its scope id as the container id.
let declaration = redeclarations.iter().find(|decl| decl.flags.is_function()).unwrap();
self.nodes().get_node(declaration.declaration).kind().get_container_scope_id()
};

let Some(container_id) = container_id else {
debug_assert!(
false,
"Found a function call or or new expr reference on a node flagged as a function or class, but the symbol's declaration node has no scope id. It should always be a container."
Expand Down Expand Up @@ -860,8 +871,8 @@ impl<'a> Symbol<'_, 'a> {
///
/// A variable scope is the closest ancestor scope (including `scope_id`
/// itself) whose kind can *outlive* the current execution slice:
/// * function‑like scopes
/// * class static blocks
/// * function‑like scopes
/// * class static blocks
/// * TypeScript namespace/module blocks
fn get_parent_variable_scope(&self, scope_id: ScopeId) -> ScopeId {
self.scoping()
Expand Down
Loading