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
- Wrap the offending case body in braces: `case 1: { const a = 1; break; }` — this is exactly what the diagnostic's help text recommends.
- Alternatively, declare the variable once before the switch and assign in each case.
- If only one branch needs local state, extract that branch into a function called from the case.
- 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
- Always brace multi-statement case bodies.
- Enable editor format-on-save with a formatter that keeps the braces.
- Declare switch-scoped variables once above the switch, not inside cases.
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
- Expected a `break` statement before `case`.
- Expected a `break` statement before `default`.
- Found a comment that would permit fallthrough, but case cann
- Unexpected {kind} declaration in the global scope.
- Global variable leak.
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/77f0b64636a99335.
Report an issue: GitHub.