Skip to content

Commit

Permalink
[[FIX]] Allow latedef in the initialiser of variable
Browse files Browse the repository at this point in the history
Fixes #2628
  • Loading branch information
lukeapage authored and jugglinmike committed Aug 23, 2015
1 parent c59ce02 commit 18f8775
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 51 deletions.
86 changes: 41 additions & 45 deletions src/jshint.js
Original file line number Diff line number Diff line change
Expand Up @@ -3521,25 +3521,6 @@ var JSHINT = (function() {
warning("E012", state.tokens.curr, state.tokens.curr.value);
}

// absorb the assignment before processing the label so that use of the label
// can be detected as before declaration
if (state.tokens.next.id === "=") {
advance("=");
if (!prefix && state.tokens.next.id === "undefined") {
warning("W080", state.tokens.prev, state.tokens.prev.value);
}
if (!prefix && peek(0).id === "=" && state.tokens.next.identifier) {
warning("W120", state.tokens.next, state.tokens.next.value);
}
// don't accept `in` in expression if prefix is used for ForIn/Of loop.
value = expression(prefix ? 120 : 10);
if (lone) {
tokens[0].first = value;
} else {
destructuringPatternMatch(names, value);
}
}

for (var t in tokens) {
if (tokens.hasOwnProperty(t)) {
t = tokens[t];
Expand All @@ -3561,6 +3542,23 @@ var JSHINT = (function() {
}
}

if (state.tokens.next.id === "=") {
advance("=");
if (!prefix && state.tokens.next.id === "undefined") {
warning("W080", state.tokens.prev, state.tokens.prev.value);
}
if (!prefix && peek(0).id === "=" && state.tokens.next.identifier) {
warning("W120", state.tokens.next, state.tokens.next.value);
}
// don't accept `in` in expression if prefix is used for ForIn/Of loop.
value = expression(prefix ? 120 : 10);
if (lone) {
tokens[0].first = value;
} else {
destructuringPatternMatch(names, value);
}
}

statement.first = statement.first.concat(names);

if (state.tokens.next.id !== ",") {
Expand Down Expand Up @@ -3614,32 +3612,6 @@ var JSHINT = (function() {

this.first = this.first.concat(names);

// absorb the assignment before processing the label so that use of the label
// can be detected as before declaration
if (state.tokens.next.id === "=") {
state.nameStack.set(state.tokens.curr);

advance("=");
if (!prefix && report && !state.funct["(loopage)"] &&
state.tokens.next.id === "undefined") {
warning("W080", state.tokens.prev, state.tokens.prev.value);
}
if (peek(0).id === "=" && state.tokens.next.identifier) {
if (!prefix && report &&
!state.funct["(params)"] ||
state.funct["(params)"].indexOf(state.tokens.next.value) === -1) {
warning("W120", state.tokens.next, state.tokens.next.value);
}
}
// don't accept `in` in expression if prefix is used for ForIn/Of loop.
value = expression(prefix ? 120 : 10);
if (lone) {
tokens[0].first = value;
} else {
destructuringPatternMatch(names, value);
}
}

for (var t in tokens) {
if (tokens.hasOwnProperty(t)) {
t = tokens[t];
Expand Down Expand Up @@ -3674,6 +3646,30 @@ var JSHINT = (function() {
}
}

if (state.tokens.next.id === "=") {
state.nameStack.set(state.tokens.curr);

advance("=");
if (!prefix && report && !state.funct["(loopage)"] &&
state.tokens.next.id === "undefined") {
warning("W080", state.tokens.prev, state.tokens.prev.value);
}
if (peek(0).id === "=" && state.tokens.next.identifier) {
if (!prefix && report &&
!state.funct["(params)"] ||
state.funct["(params)"].indexOf(state.tokens.next.value) === -1) {
warning("W120", state.tokens.next, state.tokens.next.value);
}
}
// don't accept `in` in expression if prefix is used for ForIn/Of loop.
value = expression(prefix ? 120 : 10);
if (lone) {
tokens[0].first = value;
} else {
destructuringPatternMatch(names, value);
}
}

if (state.tokens.next.id !== ",") {
break;
}
Expand Down
7 changes: 4 additions & 3 deletions src/scope-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -747,15 +747,16 @@ var scopeManager = function(state, predefined, exported, declared) {
use: function(labelName, token) {

// if resolves to current function params, then do not store usage just resolve
// this is because function(a) { var a = a; } will resolve to the param, not
// this is because function(a) { var a; a = a; } will resolve to the param, not
// to the unset var
// first check the param is used
var paramScope = _currentFunctBody["(parent)"];
if (paramScope && paramScope["(labels)"][labelName] &&
paramScope["(labels)"][labelName]["(type)"] === "param") {

// then check its not used
if (!scopeManagerInst.funct.has(labelName, { excludeParams: true })) {
// then check its not declared by a block scope variable
if (!scopeManagerInst.funct.has(labelName,
{ excludeParams: true, onlyBlockscoped: true })) {
paramScope["(labels)"][labelName]["(unused)"] = false;
}
}
Expand Down
7 changes: 7 additions & 0 deletions tests/unit/fixtures/latedef-esnext.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,10 @@ function cg() {
cj();
}
cg();
let da = () => da;
const db = () => db;
let dc = {
dd() {
return dc;
}
};
9 changes: 9 additions & 0 deletions tests/unit/fixtures/latedef.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ function foo() {
return 10;
}
}
var a = function() {
return a;
};
var b = b;
var c = {
d: function() {
return d;
}
};
16 changes: 13 additions & 3 deletions tests/unit/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -736,11 +736,21 @@ exports.undef = function (test) {

// block scope cannot use themselves in the declaration
TestRun(test)
.addError(1, "'a' was used before it was declared, which is illegal for 'let' variables.")
.addError(2, "'b' was used before it was declared, which is illegal for 'const' variables.")
// JSHint does not currently enforce the correct temporal dead zone
// semantics in this case. Once this is fixed, the following errors
// should be thrown:
//.addError(1, "'a' was used before it was declared, which is illegal for 'let' variables.")
//.addError(2, "'b' was used before it was declared, which is illegal for 'const' variables.")
.addError(5, "'e' is already defined.")
.test([
'let a = a;',
'const b = b;'
'const b = b;',
'var c = c;',
'function f(e) {',
' var e;', // the var does not overwrite the param, the param is used
' e = e || 2;',
' return e;',
'}'
], { esnext: true, undef: true });

// Regression test for GH-668.
Expand Down

0 comments on commit 18f8775

Please sign in to comment.