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).
- bisected regression to eee020d#diff-4822639e8179a5188bdfd8cfb48d9e103a7dcc1f2b1036465ab7017d4166c2a8R3246
@@ -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
@jamesdiacono
Same bug?
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);