closure-compiler icon indicating copy to clipboard operation
closure-compiler copied to clipboard

"let" within switch causes variable reference error with outer let

Open evmar opened this issue 7 years ago • 5 comments

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

evmar avatar Nov 17 '17 22:11 evmar

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.

MatrixFrog avatar Nov 17 '17 22:11 MatrixFrog

Here the conflict is between the outer function scope and the scope created by the switch.

evmar avatar Nov 17 '17 22:11 evmar

It's getting that error because you're declaring the variable again (typing let again). Instead, just type c = 5; (without let).

jphjsoares avatar Nov 18 '17 23:11 jphjsoares

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.

brad4d avatar Nov 28 '17 00:11 brad4d

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;

thheller avatar Sep 27 '22 11:09 thheller