Skip to content

Commit

Permalink
[[FEAT]] Option to assume strict mode
Browse files Browse the repository at this point in the history
Why?
1) Some JavaScript compilers (e.g. Babel) add the "use strict" directive, so
   it isn't needed that it is present in the code.
2) In node.js you can use the --use_strict flag, which runs the code using
   strict mode.

The `strict` option has now five possible values:
- true / func - there must be a `"use strict";` directive at function level
- global      - there must be a `"use strict";` directive at global level
- implied     - JSHint checks the code as if there is the `"use strict";`
  directive
- false       - disable warnings about strict mode

The `globalstrict` option is now deprecated, it should be used `strict: global`

Close #924
  • Loading branch information
nicolo-ribaudo authored and lukeapage committed Jun 27, 2015
1 parent da66f70 commit 8de8247
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 41 deletions.
50 changes: 35 additions & 15 deletions src/jshint.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ var JSHINT = (function() {
}

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

if (state.option.yui) {
Expand Down Expand Up @@ -617,6 +617,25 @@ var JSHINT = (function() {
return;
}

if (key === "strict") {
switch (val) {
case "true":
case "func":
state.option.strict = true;
break;
case "false":
state.option.strict = false;
break;
case "global":
case "implied":
state.option.strict = val;
break;
default:
error("E002", nt);
}
return;
}

var match = /^([+-])(W\d{3})$/g.exec(key);
if (match) {
// ignore for -W..., unignore for +W...
Expand Down Expand Up @@ -1557,8 +1576,7 @@ var JSHINT = (function() {

if (r && (!r.identifier || r.value !== "function") && (r.type !== "(punctuator)")) {
if (!state.isStrict() &&
state.option.globalstrict &&
state.option.strict) {
state.option.strict === "global") {
warning("E007");
}
}
Expand Down Expand Up @@ -1636,24 +1654,26 @@ var JSHINT = (function() {
}

advance();
if (state.directive[state.tokens.curr.value]) {
warning("W034", state.tokens.curr, state.tokens.curr.value);
}

if (state.tokens.curr.value === "use strict") {
if (!state.option["(explicitNewcap)"]) {
state.option.newcap = true;
}
state.option.undef = true;
var directive = state.tokens.curr.value;
if (state.directive[directive] ||
(directive === "use strict" && state.option.strict === "implied")) {
warning("W034", state.tokens.curr, directive);
}

// there's no directive negation, so always set to true
state.directive[state.tokens.curr.value] = true;
state.directive[directive] = true;

if (p.id === ";") {
advance(";");
}
}

if (state.isStrict()) {
if (!state.option["(explicitNewcap)"]) {
state.option.newcap = true;
}
state.option.undef = true;
}
}


Expand Down Expand Up @@ -5034,8 +5054,8 @@ var JSHINT = (function() {
default:
directives();

if (state.isStrict()) {
if (!state.option.globalstrict) {
if (state.directive["use strict"]) {
if (state.option.strict !== "global") {
if (!(state.option.module || state.option.node || state.option.phantom ||
state.option.browserify)) {
warning("W097", state.tokens.prev);
Expand Down
33 changes: 18 additions & 15 deletions src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -258,21 +258,6 @@ exports.bool = {
*/
singleGroups: false,

/**
* This option requires all functions to run in ECMAScript 5's strict mode.
* [Strict mode](https://developer.mozilla.org/en/JavaScript/Strict_mode)
* is a way to opt in to a restricted variant of JavaScript. Strict mode
* eliminates some JavaScript pitfalls that didn't cause errors by changing
* them to produce errors. It also fixes mistakes that made it difficult
* for the JavaScript engines to perform certain optimizations.
*
* *Note:* This option enables strict mode for function scope only. It
* *prohibits* the global scoped strict mode because it might break
* third-party widgets on your page. If you really want to use global
* strict mode, see the *globalstrict* option.
*/
strict : true,

/**
* When set to true, the use of VariableStatements are forbidden.
* For example:
Expand Down Expand Up @@ -375,6 +360,8 @@ exports.bool = {
* recommended.
*
* For more info about strict mode see the `strict` option.
*
* @deprecated Use `strict: "global"`.
*/
globalstrict: true,

Expand Down Expand Up @@ -882,6 +869,22 @@ exports.val = {
*/
shadow : false,

/**
* This option requires the code to run in ECMAScript 5's strict mode.
* [Strict mode](https://developer.mozilla.org/en/JavaScript/Strict_mode)
* is a way to opt in to a restricted variant of JavaScript. Strict mode
* eliminates some JavaScript pitfalls that didn't cause errors by changing
* them to produce errors. It also fixes mistakes that made it difficult
* for the JavaScript engines to perform certain optimizations.
*
* - "func" - there must be a `"use strict";` directive at function level
* - "global" - there must be a `"use strict";` directive at global level
* - "implied" - lint the code as if there is the `"use strict";` directive
* - false - disable warnings about strict mode
* - true - same as `"func"`
*/
strict : true,

/**
* This option warns when you define and never use your variables. It is very
* useful for general code cleanup, especially when used in addition to
Expand Down
2 changes: 1 addition & 1 deletion src/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ var state = {
*/
isStrict: function() {
return this.directive["use strict"] || this.inClassBody ||
this.option.module;
this.option.module || this.option.strict === "implied";
},

// Assumption: chronologically ES3 < ES5 < ES6/ESNext < Moz
Expand Down
44 changes: 34 additions & 10 deletions tests/unit/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -1434,38 +1434,62 @@ exports.sub = function (test) {
exports.strict = function (test) {
var code = "(function (test) { return; }());";
var code1 = '(function (test) { "use strict"; return; }());';
var code2 = "var obj = Object({ foo: 'bar' });";
var code3 = "'use strict'; \n function hello() { return; }";
var src = fs.readFileSync(__dirname + '/fixtures/strict_violations.js', 'utf8');
var src2 = fs.readFileSync(__dirname + '/fixtures/strict_incorrect.js', 'utf8');
var src3 = fs.readFileSync(__dirname + '/fixtures/strict_newcap.js', 'utf8');

TestRun(test).test(code, {es3: true});
TestRun(test).test(code1, {es3: true});

TestRun(test)
.addError(1, 'Missing "use strict" statement.')
.test(code, { es3: true, strict: true });
var run = TestRun(test)
.addError(1, 'Missing "use strict" statement.');
run.test(code, { es3: true, strict: true });
run.test(code, { es3: true, strict: "func" });
run.test(code, { es3: true, strict: "global" });
TestRun(test).test(code, { es3: true, strict: "implied" });

TestRun(test).test(code1, { es3: true, strict: true });
TestRun(test).test(code1, { es3: true, strict: "func" });
TestRun(test).test(code1, { es3: true, strict: "global" });
TestRun(test)
.addError(1, 'Unnecessary directive "use strict".')
.test(code1, { es3: true, strict: "implied" });

// Test for strict mode violations
TestRun(test)
run = TestRun(test)
.addError(4, 'Possible strict violation.')
.addError(7, 'Strict violation.')
.addError(8, 'Strict violation.')
.test(src, { es3: true, strict: true });
.addError(8, 'Strict violation.');
run.test(src, { es3: true, strict: true });
run.test(src, { es3: true, strict: "func" });
run.test(src, { es3: true, strict: "global" });

TestRun(test)
run = TestRun(test)
.addError(4, 'Expected an assignment or function call and instead saw an expression.')
.addError(9, 'Missing semicolon.')
.addError(28, 'Expected an assignment or function call and instead saw an expression.')
.addError(53, 'Expected an assignment or function call and instead saw an expression.')
.test(src2, { es3: true, strict: false });
.addError(53, 'Expected an assignment or function call and instead saw an expression.');
run.test(src2, { es3: true, strict: false });

TestRun(test)
.addError(6, "Missing 'new' prefix when invoking a constructor.")
.test(src3, {es3 : true});

TestRun(test).test("var obj = Object({ foo: 'bar' });", { es3: true, strict: true });
TestRun(test).test(code2, { es3: true, strict: true });
TestRun(test).test(code2, { es3: true, strict: "func" });
TestRun(test)
.addError(1, 'Missing "use strict" statement.')
.test(code2, { es3: true, strict: "global" });

TestRun(test).test(code3, { strict: "global"});
run = TestRun(test)
.addError(1, 'Use the function form of "use strict".');
run.test(code3, { strict: true });
run.test(code3, { strict: "func" });
run.addError(1, 'Unnecessary directive "use strict".')
.test(code3, { strict: "implied" });

test.done();
};
Expand Down

0 comments on commit 8de8247

Please sign in to comment.