closure-compiler
closure-compiler copied to clipboard
"let" within switch causes variable reference error with outer let
function foo() {
let c = 3;
switch (c) {
case 4:
let c = 5;
break;
}
}
Expected: should be accepted
Actual: JSC_REFERENCE_BEFORE_DECLARE_ERROR: Illegal variable reference before declaration: c at line 3 character 10 switch (c) { ^
Compiler link:
https://closure-compiler.appspot.com/home#code%3D%252F%252F%2520%253D%253DClosureCompiler%253D%253D%250A%252F%252F%2520%2540compilation_level%2520SIMPLE_OPTIMIZATIONS%250A%252F%252F%2520%2540output_file_name%2520default.js%250A%252F%252F%2520%253D%253D%252FClosureCompiler%253D%253D%250A%250A%250Afunction%2520foo()%2520%257B%250A%2520%2520let%2520c%2520%253D%25203%253B%250A%2520%2520switch%2520(c)%2520%257B%250A%2520%2520%2520%2520case%25204%253A%250A%2520%2520%2520%2520%2520%2520let%2520c%2520%253D%25205%253B%250A%2520%2520%2520%2520%2520%2520break%253B%250A%2520%2520%257D%250A%257D
IIRC we have one scope per switch statement. I guess technically we should have one for the switch statement and one for the body of the switch statement.
Here the conflict is between the outer function scope and the scope created by the switch.
It's getting that error because you're declaring the variable again (typing let again). Instead, just type c = 5; (without let).
The compiler is evaluating the switch expression within the scope of the switch statement, causing variables declared inside the statement to shadow outer scope variables.
This turns out to be rather difficult to fix, because our current AST shape for the switch statement doesn't provide a node that encloses the cases but not the switch expression. The switch node has the switch expression as its first child, then each case is a separate, following child. This doesn't give us anywhere to "hang" the variable scope correctly.
Since we expect cases in which this causes problems to be rare and such code is confusing to humans also, we're considering fixing this to be a low priority.
This problem can also be re-produced with a xterm.js (version 5.0.0).
npm install [email protected] google-closure-compiler
npx google-closure-compiler --js node_modules/xterm/lib/xterm.js
Results in
node_modules/xterm/lib/xterm.js:1:4095:
Originally at:
node_modules/xterm/lib/webpack://xterm/./src/browser/AccessibilityManager.ts:172:6: ERROR - [JSC_REFERENCE_BEFORE_DECLARE_ERROR] Illegal variable reference before declaration: e
1 error(s), 0 warning(s)
Since the source maps don't really help here I ran the file through prettier first an removed the source map. This yields a slightly more useful error.
$ npx google-closure-compiler --js xterm.js
xterm.js:2580:24: ERROR - [JSC_REFERENCE_BEFORE_DECLARE_ERROR] Illegal variable reference before declaration: e
2580| switch (e) {
^
xterm.js:7689:65: ERROR - [JSC_REFERENCE_BEFORE_DECLARE_ERROR] Illegal variable reference before declaration: e
7689| (this._restrictCursor(this._bufferService.cols), e.params[0])
^
xterm.js:8526:22: ERROR - [JSC_REFERENCE_BEFORE_DECLARE_ERROR] Illegal variable reference before declaration: e
8526| switch (e.params[0]) {
^
3 error(s), 1 warning(s)
All related to switch but none of them related to let. At the top of the file there is a var defitionion for e
return (() => {
"use strict";
var e = {
4567: (e, t, i) => {
which then seems to clash with function arguments (eg. first one)
_reportWindowsOptions(e) {
if (this._renderService)
switch (e) {
case a.WindowsOptionsReportType.GET_WIN_SIZE_PIXELS:
const e =
this._renderService.dimensions.canvasWidth.toFixed(0),
t =
this._renderService.dimensions.canvasHeight.toFixed(0);
this.coreService.triggerDataEvent(
`${o.C0.ESC}[4;${t};${e}t`
);
break;