oxc-project/oxc · error · OxcDiagnostic

Unexpected lexical declaration in case block.

Error message

Unexpected lexical declaration in case block.

What it means

Diagnostic from the oxlint rule `no-case-declarations` (crates/oxc_linter/src/rules/eslint/no_case_declarations.rs). It fires when a lexical declaration (`let`, `const`, `class`, or `function` declaration) appears directly in a `switch` case clause without a block. All clauses of one switch share a single block scope, so a lexical declaration in one case is visible (and collides) across every other case in the same switch, which easily produces SyntaxErrors or surprising temporal-dead-zone behavior.

Source

Thrown at crates/oxc_linter/src/rules/eslint/no_case_declarations.rs:12

use oxc_ast::{
    AstKind,
    ast::{Statement, SwitchCase, VariableDeclarationKind},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;

use crate::{AstNode, context::LintContext, rule::Rule};

fn no_case_declarations_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected lexical declaration in case block.")
        .with_help("Wrap the case body in braces `{}` to create an explicit block scope for the lexical declaration.")
        .with_label(span)
}

#[derive(Debug, Default, Clone)]
pub struct NoCaseDeclarations;

declare_oxc_lint!(
    /// ### What it does
    ///
    /// Disallow lexical declarations in case clauses.
    ///
    /// ### Why is this bad?
    ///
    /// The reason is that the lexical declaration is visible
    /// in the entire switch block but it only gets initialized when it is assigned,
    /// which will only happen if the case where it is defined is reached.
    ///

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Wrap the offending case body in braces: `case 1: { const a = 1; break; }` — this is exactly what the diagnostic's help text recommends.
  2. Alternatively, declare the variable once before the switch and assign in each case.
  3. If only one branch needs local state, extract that branch into a function called from the case.
  4. Keep the practice of always bracing multi-statement cases to prevent recurrence.

Example fix

// before
switch (kind) {
  case 'a':
    const map = { x: 1 };
    break;
  case 'b':
    const map2 = { x: 2 };
    break;
}

// after
switch (kind) {
  case 'a': {
    const map = { x: 1 };
    break;
  }
  case 'b': {
    const map2 = { x: 2 };
    break;
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// Quick gate: lexical declarations directly inside a case clause
function caseHasBareLexical(stmts) {
  return stmts.some(s => /^(let|const|class|function)\b/.test(s.source || ''));
}
// better: let oxlint's AST-based check run in a pre-commit hook

Prevention

When it happens

Trigger: A SwitchCase whose consecutive statements include a VariableDeclaration with kind `let`/`const` (or class/function declaration) and are not wrapped in a BlockStatement — e.g. `switch (x) { case 1: let a = 1; break; case 2: let a = 2; }`.

Common situations: Adding a new case with `const` to an existing switch; duplicated variable names across cases (classic `SyntaxError: Identifier 'a' has already been declared` at runtime); refactoring if/else chains into switches during lint adoption.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/77f0b64636a99335. Report an issue: GitHub.