Skip to content

Commit

Permalink
[[FIX]] Do not fail on valid configurations
Browse files Browse the repository at this point in the history
When interpreting configuration values for strict-mode-related options,
do not modify user-specified values. Instead, preserve the values as
specified and encapsulate interpretation logic into stateless helper
methods on the `state` object.

This pattern allows JSHint to consistently issue warnings about invalid
configurations by ensuring that at any given time, all option values in
the `state` object reflect values explicitly specified by the user.

As noted in the in-line documentation, this approach required making
explicit certain sub-optimal behaviors which were previously implicit.
These concerns would best be addressed in a later patch, distinct from
the regression that prompted this changeset.
  • Loading branch information
jugglinmike committed Jan 23, 2016
1 parent 200c738 commit 2fb3c24
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 13 deletions.
15 changes: 2 additions & 13 deletions src/jshint.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,6 @@ var JSHINT = (function() {
}

if (state.option.module) {
if (state.option.strict === true) {
state.option.strict = "global";
}
/**
* TODO: Extend this restriction to *all* ES6-specific options.
*/
Expand Down Expand Up @@ -302,10 +299,6 @@ var JSHINT = (function() {
combine(predefined, vars.wsh);
}

if (state.option.globalstrict && state.option.strict !== false) {
state.option.strict = "global";
}

if (state.option.yui) {
combine(predefined, vars.yui);
}
Expand Down Expand Up @@ -1647,8 +1640,7 @@ var JSHINT = (function() {
if (r && !(r.identifier && r.value === "function") &&
!(r.type === "(punctuator)" && r.left &&
r.left.identifier && r.left.value === "function")) {
if (!state.isStrict() &&
state.option.strict === "global") {
if (!state.isStrict() && state.stmtMissingStrict()) {
warning("E007");
}
}
Expand Down Expand Up @@ -5296,10 +5288,7 @@ var JSHINT = (function() {
directives();

if (state.directive["use strict"]) {
if (state.option.strict !== "global" &&
!((state.option.strict === true || !state.option.strict) &&
(state.option.globalstrict || state.option.module || state.option.node ||
state.option.phantom || state.option.browserify))) {
if (!state.allowsGlobalUsd()) {
warning("W097", state.tokens.prev);
}
}
Expand Down
36 changes: 36 additions & 0 deletions src/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,42 @@ var state = {
this.option.module || this.option.strict === "implied";
},

/**
* Determine if the current state warrants a warning for statements outside
* of strict mode code.
*
* While emitting warnings based on function scope would be more intuitive
* (and less noisy), JSHint observes statement-based semantics in order to
* preserve legacy behavior.
*
* This method does not take the state of the parser into account, making no
* distinction between global code and function code. Because the "missing
* 'use strict'" warning is *also* reported at function boundaries, this
* function interprets `strict` option values `true` and `undefined` as
* equivalent.
*/
stmtMissingStrict: function() {
if (this.option.strict === "global") {
return true;
}

if (this.option.strict === false) {
return false;
}

if (this.option.globalstrict) {
return true;
}

return false;
},

allowsGlobalUsd: function() {
return this.option.strict === "global" || this.option.globalstrict ||
this.option.module || this.option.node || this.option.phantom ||
this.option.browserify;
},

// Assumption: chronologically ES3 < ES5 < ES6 < Moz

inMoz: function() {
Expand Down
16 changes: 16 additions & 0 deletions tests/unit/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -1905,6 +1905,22 @@ exports.globalstrict = function (test) {
TestRun(test, "gh-2661")
.test("'use strict';", { strict: false, globalstrict: true });

TestRun(test, "gh-2836 (1)")
.test([
"// jshint globalstrict: true",
// The specific option set by the following directive is not relevant.
// Any option set by another directive will trigger the regression.
"// jshint undef: true"
]);

TestRun(test, "gh-2836 (2)")
.test([
"// jshint strict: true, globalstrict: true",
// The specific option set by the following directive is not relevant.
// Any option set by another directive will trigger the regression.
"// jshint undef: true"
]);

test.done();
};

Expand Down

0 comments on commit 2fb3c24

Please sign in to comment.