jslint-org / jslint

JSLint, The JavaScript Code Quality and Coverage Tool

Home Page:https://www.jslint.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Regression: detect variable usage before initialization

bunglegrind opened this issue · comments

As the title stated:

no error/warning in jslint

const a = b + 3;
const b = 1;

but, of course, when executed (node 20):


const a = b + 3;
          ^

ReferenceError: Cannot access 'b' before initialization

Is it possible to detect such kind of mistakes via linting?

Well, just tested on the branch-v2014.7.8 version (with var instead of const obviously) and jslint warns about the uninitialized variable. So, I'm assuming it's a regression, not a new feature... (version 2020.11.6 however does not warn).

@@ -3220,7 +3241,9 @@ function do_var() {
             }
             enroll(name, "variable", is_const);
             if (next_token.id === "=" || is_const) {
+                implied_strict = true;
                 advance("=");
+                name.dead = false;
                 name.init = true;
                 name.expression = expression(0);
             }
  • am hesitant to revert, since i don't fully understand its side-effects

Sorry, I don't have any clue.
The only suggestion I have is to write tests (many?) in order to reduce other regressions chance

Yes. Funny that nobody noticed it for 4 years?

  • @jamesdiacono can you submit a patch to beta branch and take credit for work already done?

  • here's a starting point to apply to current jslint.mjs (admittedly, i don't fully understand it ^^;;;)

git diff
diff --git a/jslint.mjs b/jslint.mjs
index 8abd882..d8f7413 100644
--- a/jslint.mjs
+++ b/jslint.mjs
@@ -5147,6 +5147,7 @@ function jslint_phase3_parse(state) {
                 the_label.dead = false;
                 the_label.init = true;
                 the_statement = parse_statement();
+                the_label.dead = true;
                 functionage.statement_prv = the_statement;
                 the_statement.label = the_label;
                 the_statement.statement = true;
@@ -5193,9 +5194,6 @@ function jslint_phase3_parse(state) {
             }
             semicolon();
         }
-        if (the_label !== undefined) {
-            the_label.dead = true;
-        }
         return the_statement;
     }

@@ -6976,7 +6974,6 @@ function jslint_phase3_parse(state) {
                         the_variable.names.push(name);
                         enroll(name, "variable", mode_const);
                     }
-                    name.dead = false;
                     name.init = true;
                     if (token_nxt.id === "=") {

@@ -7028,7 +7025,6 @@ function jslint_phase3_parse(state) {
                     advance();
                     the_variable.names.push(name);
                     enroll(name, "variable", mode_const);
-                    name.dead = false;
                     name.init = true;
                     if (ellipsis) {
                         name.ellipsis = true;
@@ -7062,7 +7058,6 @@ function jslint_phase3_parse(state) {
                 enroll(name, "variable", mode_const);
                 if (token_nxt.id === "=" || mode_const) {
                     advance("=");
-                    name.dead = false;
                     name.init = true;
                     name.expression = parse_expression(0);
                 }
  • can help on writing / fixing tests once the initial pr is created

You are absolutely welcome to use any of the commits from my fork, no need to credit me.

@kaizhu256 about the tests...I don't understand how they work. I couldn't find any hint in the documentation.

For instance, how I write that

const a = b;
const b = 1;

should raise an error?

  • the following patch would add test-cases for const

  • EDIT: sorry for lack of documentation, but but here's general signature:

// test_cause:
[
    "<expression>",
    "<calling function>",
    "<warning_code>",
    "<warning_argument>",
    <column where error occurred>
]
--- a/jslint.mjs
+++ b/jslint.mjs
@@ -5147,6 +5147,7 @@ function jslint_phase3_parse(state) {
                 the_label.dead = false;
                 the_label.init = true;
                 the_statement = parse_statement();
+                the_label.dead = true;
                 functionage.statement_prv = the_statement;
                 the_statement.label = the_label;
                 the_statement.statement = true;
@@ -5193,9 +5194,6 @@ function jslint_phase3_parse(state) {
             }
             semicolon();
         }
-        if (the_label !== undefined) {
-            the_label.dead = true;
-        }
         return the_statement;
     }

@@ -6969,15 +6967,17 @@ function jslint_phase3_parse(state) {
                         the_variable.names.push(name);
                         survey(name);
                         enroll(name, "variable", mode_const);
-
                         advance();
                         the_brace.open = true;
                     } else {
                         the_variable.names.push(name);
                         enroll(name, "variable", mode_const);
                     }
-                    name.dead = false;
                     name.init = true;
+
+// test_cause:
+// ["const {aa}=bb;\nconst bb=0;", "lookup", "out_of_scope_a", "bb", 12]
+
                     if (token_nxt.id === "=") {

 // test_cause:
@@ -7028,8 +7028,11 @@ function jslint_phase3_parse(state) {
                     advance();
                     the_variable.names.push(name);
                     enroll(name, "variable", mode_const);
-                    name.dead = false;
                     name.init = true;
+
+// test_cause:
+// ["const [aa]=bb;\nconst bb=0;", "lookup", "out_of_scope_a", "bb", 12]
+
                     if (ellipsis) {
                         name.ellipsis = true;
                         break;
@@ -7062,8 +7065,11 @@ function jslint_phase3_parse(state) {
                 enroll(name, "variable", mode_const);
                 if (token_nxt.id === "=" || mode_const) {
                     advance("=");
-                    name.dead = false;
                     name.init = true;
+
+// test_cause:
+// ["const aa=bb;\nconst bb=0;", "lookup", "out_of_scope_a", "bb", 10]
+
                     name.expression = parse_expression(0);
                 }
                 the_variable.names.push(name);