Skip to content

Commit

Permalink
[[FEAT]] Enforce ES2016 restriction on USD
Browse files Browse the repository at this point in the history
ECMAScript 2016 introduced an early error for any function situated in
non-strict code whose parameter list is non-simple and whose body
contains a "use strict" directive. Enforce this restriction when
interpreting input according to that revision of the specification.
  • Loading branch information
jugglinmike committed Dec 23, 2018
1 parent 21b8731 commit 2c2025b
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 22 deletions.
27 changes: 24 additions & 3 deletions src/jshint.js
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,18 @@ var JSHINT = (function() {
warning("W034", state.tokens.curr, directive);
}

// From ECMAScript 2016:
//
// > 14.1.2 Static Semantics: Early Errors
// >
// > [...]
// > - It is a Syntax Error if ContainsUseStrict of FunctionBody is true
// > and IsSimpleParameterList of FormalParameters is false.
if (directive === "use strict" && state.inES7() &&
!state.funct["(global)"] && state.funct["(hasSimpleParams)"] === false) {
error("E065", state.tokens.curr);
}

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

Expand Down Expand Up @@ -2838,7 +2850,8 @@ var JSHINT = (function() {
* single-argument shorthand.
* @param {bool} [options.parsedOpening] Whether the opening parenthesis has
* already been parsed.
* @returns {{ arity: number, params: Array.<string>}}
*
* @returns {{ arity: number, params: Array.<string>, isSimple: boolean }}
*/
function functionparams(options) {
var next;
Expand All @@ -2850,10 +2863,11 @@ var JSHINT = (function() {
var pastRest = false;
var arity = 0;
var loneArg = options && options.loneArg;
var hasDestructuring = false;

if (loneArg && loneArg.identifier === true) {
state.funct["(scope)"].addParam(loneArg.value, loneArg);
return { arity: 1, params: [ loneArg.value ] };
return { arity: 1, params: [ loneArg.value ], isSimple: true };
}

next = state.tokens.next;
Expand All @@ -2877,6 +2891,7 @@ var JSHINT = (function() {
var currentParams = [];

if (_.includes(["{", "["], state.tokens.next.id)) {
hasDestructuring = true;
tokens = destructuringPattern();
for (t in tokens) {
t = tokens[t];
Expand Down Expand Up @@ -2929,7 +2944,11 @@ var JSHINT = (function() {
parseComma();
} else {
advance(")", next);
return { arity: arity, params: paramsIds };
return {
arity: arity,
params: paramsIds,
isSimple: !hasDestructuring && !pastRest && !pastDefault
};
}
}
}
Expand Down Expand Up @@ -3105,10 +3124,12 @@ var JSHINT = (function() {

if (paramsInfo) {
state.funct["(params)"] = paramsInfo.params;
state.funct["(hasSimpleParams)"] = paramsInfo.isSimple;
state.funct["(metrics)"].arity = paramsInfo.arity;
state.funct["(metrics)"].verifyMaxParametersPerFunction();
} else {
state.funct["(metrics)"].arity = 0;
state.funct["(hasSimpleParams)"] = true;
}

if (isArrow) {
Expand Down
4 changes: 3 additions & 1 deletion src/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ var errors = {
E061: "Invalid position for 'yield' expression (consider wrapping in parenthesis).",
E062: "Rest parameter does not a support default value.",
E063: "Super property may only be used within method bodies.",
E064: "Super call may only be used within class method bodies."
E064: "Super call may only be used within class method bodies.",
E065: "Functions defined outside of strict mode with non-simple parameter lists may not " +
"enable strict mode."
};

var warnings = {
Expand Down
18 changes: 0 additions & 18 deletions tests/test262/expectations.txt
Original file line number Diff line number Diff line change
Expand Up @@ -568,8 +568,6 @@ test/language/expressions/function/dstr-dflt-ary-ptrn-rest-obj-prop-id.js(strict
test/language/expressions/function/param-dflt-yield-non-strict.js(default)
test/language/expressions/function/scope-param-rest-elem-var-close.js(default)
test/language/expressions/function/scope-param-rest-elem-var-open.js(default)
test/language/expressions/function/use-strict-with-non-simple-param.js(default)
test/language/expressions/function/use-strict-with-non-simple-param.js(strict mode)
test/language/expressions/generators/dstr-ary-ptrn-rest-ary-elem.js(default)
test/language/expressions/generators/dstr-ary-ptrn-rest-ary-elem.js(strict mode)
test/language/expressions/generators/dstr-ary-ptrn-rest-ary-elision.js(default)
Expand Down Expand Up @@ -602,8 +600,6 @@ test/language/expressions/generators/param-dflt-yield.js(default)
test/language/expressions/generators/param-dflt-yield.js(strict mode)
test/language/expressions/generators/scope-param-rest-elem-var-close.js(default)
test/language/expressions/generators/scope-param-rest-elem-var-open.js(default)
test/language/expressions/generators/use-strict-with-non-simple-param.js(default)
test/language/expressions/generators/use-strict-with-non-simple-param.js(strict mode)
test/language/expressions/generators/yield-as-function-expression-binding-identifier.js(default)
test/language/expressions/generators/yield-as-identifier-in-nested-function.js(default)
test/language/expressions/generators/yield-as-logical-or-expression.js(default)
Expand Down Expand Up @@ -893,13 +889,9 @@ test/language/expressions/object/method-definition/async-super-call-body.js(defa
test/language/expressions/object/method-definition/async-super-call-body.js(strict mode)
test/language/expressions/object/method-definition/async-super-call-param.js(default)
test/language/expressions/object/method-definition/async-super-call-param.js(strict mode)
test/language/expressions/object/method-definition/early-errors-object-method-NSPL-with-USD.js(default)
test/language/expressions/object/method-definition/early-errors-object-method-NSPL-with-USD.js(strict mode)
test/language/expressions/object/method-definition/generator-param-init-yield.js(default)
test/language/expressions/object/method-definition/generator-prop-name-yield-expr.js(default)
test/language/expressions/object/method-definition/generator-prop-name-yield-id.js(default)
test/language/expressions/object/method-definition/generator-use-strict-with-non-simple-param.js(default)
test/language/expressions/object/method-definition/generator-use-strict-with-non-simple-param.js(strict mode)
test/language/expressions/object/method-definition/gen-meth-dflt-params-duplicates.js(default)
test/language/expressions/object/method-definition/meth-dflt-params-duplicates.js(default)
test/language/expressions/object/method-definition/name-param-id-yield.js(default)
Expand All @@ -908,10 +900,6 @@ test/language/expressions/object/method-definition/name-prop-name-yield-expr.js(
test/language/expressions/object/method-definition/name-prop-name-yield-id.js(default)
test/language/expressions/object/method-definition/object-method-returns-promise.js(default)
test/language/expressions/object/method-definition/object-method-returns-promise.js(strict mode)
test/language/expressions/object/method-definition/setter-use-strict-with-non-simple-param.js(default)
test/language/expressions/object/method-definition/setter-use-strict-with-non-simple-param.js(strict mode)
test/language/expressions/object/method-definition/use-strict-with-non-simple-param.js(default)
test/language/expressions/object/method-definition/use-strict-with-non-simple-param.js(strict mode)
test/language/expressions/object/method-definition/yield-as-expression-with-rhs.js(default)
test/language/expressions/object/method-definition/yield-as-expression-with-rhs.js(strict mode)
test/language/expressions/object/method-definition/yield-as-expression-without-rhs.js(default)
Expand Down Expand Up @@ -961,8 +949,6 @@ test/language/expressions/arrow-function/syntax/early-errors/asi-restriction-inv
test/language/expressions/arrow-function/syntax/early-errors/asi-restriction-invalid-parenless-parameters-expression-body.js(strict mode)
test/language/expressions/arrow-function/syntax/early-errors/asi-restriction-invalid-parenless-parameters.js(default)
test/language/expressions/arrow-function/syntax/early-errors/asi-restriction-invalid-parenless-parameters.js(strict mode)
test/language/expressions/arrow-function/syntax/early-errors/use-strict-with-non-simple-param.js(default)
test/language/expressions/arrow-function/syntax/early-errors/use-strict-with-non-simple-param.js(strict mode)
test/language/statements/break/12.8-1.js(default)
test/language/statements/break/12.8-1.js(strict mode)
test/language/statements/async-function/declaration-returns-promise.js(default)
Expand Down Expand Up @@ -1256,8 +1242,6 @@ test/language/statements/generators/param-dflt-yield.js(default)
test/language/statements/generators/param-dflt-yield.js(strict mode)
test/language/statements/generators/scope-param-rest-elem-var-close.js(default)
test/language/statements/generators/scope-param-rest-elem-var-open.js(default)
test/language/statements/generators/use-strict-with-non-simple-param.js(default)
test/language/statements/generators/use-strict-with-non-simple-param.js(strict mode)
test/language/statements/generators/yield-as-function-expression-binding-identifier.js(default)
test/language/statements/generators/yield-as-generator-declaration-binding-identifier.js(default)
test/language/statements/generators/yield-as-identifier-in-nested-function.js(default)
Expand Down Expand Up @@ -1308,8 +1292,6 @@ test/language/statements/function/dstr-dflt-ary-ptrn-rest-obj-prop-id.js(strict
test/language/statements/function/param-dflt-yield-non-strict.js(default)
test/language/statements/function/scope-param-rest-elem-var-close.js(default)
test/language/statements/function/scope-param-rest-elem-var-open.js(default)
test/language/statements/function/use-strict-with-non-simple-param.js(default)
test/language/statements/function/use-strict-with-non-simple-param.js(strict mode)
test/language/statements/return/S12.9_A1_T1.js(default)
test/language/statements/return/S12.9_A1_T1.js(strict mode)
test/language/statements/return/S12.9_A1_T10.js(default)
Expand Down
146 changes: 146 additions & 0 deletions tests/unit/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -3421,6 +3421,152 @@ exports["destructuring function default values"] = function (test) {
test.done();
};

exports["non-simple parameter list strict transition"] = function (test) {
var noTransitionNonStrict = [
"function f() {}",
"function f(x) {}",
"var a = x => {};",
"function f({ x }) {}",
"function f([ x ]) {}",
"function f(...x) {}",
"function f(x = 0) {}"
];

TestRun(test, "no transition: ES6 & non-strict mode")
.test(noTransitionNonStrict, { esversion: 6 });
TestRun(test, "no transition: ES7 & non-strict mode")
.test(noTransitionNonStrict, { esversion: 7 });

var noTransitionStrict = [
"'use strict';",
"function f() {",
" 'use strict';",
"}",
"function f(x) {",
" 'use strict';",
"}",
"var a = x => {",
" 'use strict';",
"};",
"function f({ x }) {",
" 'use strict';",
"}",
"function f([ x ]) {",
" 'use strict';",
"}",
"function f(...x) {",
" 'use strict';",
"}",
"function f(x = 0) {",
" 'use strict';",
"}"
];

TestRun(test, "no transition: ES6 & strict mode")
.addError(1, 1, "Use the function form of \"use strict\".")
.addError(3, 3, "Unnecessary directive \"use strict\".")
.addError(6, 3, "Unnecessary directive \"use strict\".")
.addError(9, 3, "Unnecessary directive \"use strict\".")
.addError(12, 3, "Unnecessary directive \"use strict\".")
.addError(15, 3, "Unnecessary directive \"use strict\".")
.addError(18, 3, "Unnecessary directive \"use strict\".")
.addError(21, 3, "Unnecessary directive \"use strict\".")
.test(noTransitionStrict, { esversion: 6 });
TestRun(test, "no transition: ES7 & strict mode")
.addError(1, 1, "Use the function form of \"use strict\".")
.addError(3, 3, "Unnecessary directive \"use strict\".")
.addError(6, 3, "Unnecessary directive \"use strict\".")
.addError(9, 3, "Unnecessary directive \"use strict\".")
.addError(12, 3, "Unnecessary directive \"use strict\".")
.addError(12, 3, "Functions defined outside of strict mode with non-simple parameter lists may not enable strict mode.")
.addError(15, 3, "Unnecessary directive \"use strict\".")
.addError(15, 3, "Functions defined outside of strict mode with non-simple parameter lists may not enable strict mode.")
.addError(18, 3, "Unnecessary directive \"use strict\".")
.addError(18, 3, "Functions defined outside of strict mode with non-simple parameter lists may not enable strict mode.")
.addError(21, 3, "Unnecessary directive \"use strict\".")
.addError(21, 3, "Functions defined outside of strict mode with non-simple parameter lists may not enable strict mode.")
.test(noTransitionStrict, { esversion: 7 });

var directTransition = [
"function f() {",
" 'use strict';",
"}",
"function f(x) {",
" 'use strict';",
"}",
"var a = x => {",
" 'use strict';",
"};",
"function f({ x }) {",
" 'use strict';",
"}",
"function f([ x ]) {",
" 'use strict';",
"}",
"function f(...x) {",
" 'use strict';",
"}",
"function f(x = 0) {",
" 'use strict';",
"}"
];

TestRun(test, "direct transition: ES6")
.test(directTransition, { esversion: 6 });

TestRun(test, "direct transition: ES7")
.addError(11, 3, "Functions defined outside of strict mode with non-simple parameter lists may not enable strict mode.")
.addError(14, 3, "Functions defined outside of strict mode with non-simple parameter lists may not enable strict mode.")
.addError(17, 3, "Functions defined outside of strict mode with non-simple parameter lists may not enable strict mode.")
.addError(20, 3, "Functions defined outside of strict mode with non-simple parameter lists may not enable strict mode.")
.test(directTransition, { esversion: 7 });

var indirectTransition = [
"function f() {",
" function g() {",
" 'use strict';",
" }",
"}",
"function f(x) {",
" function g() {",
" 'use strict';",
" }",
"}",
"var a = x => {",
" function g() {",
" 'use strict';",
" }",
"};",
"function f({ x }) {",
" function g() {",
" 'use strict';",
" }",
"}",
"function f([ x ]) {",
" function g() {",
" 'use strict';",
" }",
"}",
"function f(...x) {",
" function g() {",
" 'use strict';",
" }",
"}",
"function f(x = 0) {",
" function g() {",
" 'use strict';",
" }",
"}",
];

TestRun(test, "indirect transition: ES6")
.test(indirectTransition, { esversion: 6 });
TestRun(test, "indirect transition: ES7")
.test(indirectTransition, { esversion: 7 });

test.done();
};

exports["non var destructuring assignment statement"] = function (test) {
var codeValid = [
"let b;",
Expand Down

0 comments on commit 2c2025b

Please sign in to comment.