Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"use strict" in ES3 #86

Closed
mdjermanovic opened this issue Feb 1, 2022 · 4 comments · Fixed by #87
Closed

"use strict" in ES3 #86

mdjermanovic opened this issue Feb 1, 2022 · 4 comments · Fixed by #87

Comments

@mdjermanovic
Copy link
Member

Per eslint/eslint#15456, "use strict" directives should not apply in ES3.

eslint-scope currently does apply "use strict" directives in ES3 mode. Given the above, that behavior looks like a bug.

This is observable with no-invalid-this rule, which uses Scope#isStrict.

/* eslint no-invalid-this: "error" */

function foo() {
    "use strict";
    this;
}

This rule aims to report undefined this. Lowercase functions are assumed to be called without this. If the function is strict code, then this is undefined and it should be reported. If the function is non-strict code, then this refers to the global object and therefore it shouldn't be reported.

Online Demo with ES3 - the rule reports error, which seems wrong because this isn't strict code in ES3.

@eslint-github-bot eslint-github-bot bot added this to Needs Triage in Triage Feb 1, 2022
@mdjermanovic mdjermanovic moved this from Needs Triage to Feedback Needed in Triage Feb 1, 2022
@nzakas
Copy link
Member

nzakas commented Feb 2, 2022

It seems like there are multiple areas trying to track strict mode.

isStrictModeSupported:

isStrictModeSupported() {

useDirective:

__useDirective() {

isStrictScope:

function isStrictScope(scope, block, isMethodDefinition, useDirective) {

Can you narrow down where the problem is coming from? I’m guessing from useDirective, which doesn’t seem necessary as a separate option from ecmaVersion.

@mdjermanovic
Copy link
Member Author

__useDirective() returns the value of the configuration option directive.

(by "configuration option" I mean an option passed to analyze() on public API)

It doesn't control whether or not "use strict" directives will be processed and applied. It controls only how will eslint-scope technically look for "use strict" directives in given AST.

When the option is true, eslint-scope looks for DirectiveStatement nodes:

if (useDirective) {
for (let i = 0, iz = body.body.length; i < iz; ++i) {
const stmt = body.body[i];
if (stmt.type !== Syntax.DirectiveStatement) {
break;
}
if (stmt.raw === "\"use strict\"" || stmt.raw === "'use strict'") {
return true;
}
}
} else {

That's probably an alternative AST spec that was actual when escope was originally written. ESTree spec doesn't have DirectiveStatement nodes.

When the option is false (default), eslint-scope looks for ExpressionStatement nodes:

eslint-scope/lib/scope.js

Lines 97 to 119 in ceb8bdd

} else {
for (let i = 0, iz = body.body.length; i < iz; ++i) {
const stmt = body.body[i];
if (stmt.type !== Syntax.ExpressionStatement) {
break;
}
const expr = stmt.expression;
if (expr.type !== Syntax.Literal || typeof expr.value !== "string") {
break;
}
if (expr.raw !== null && expr.raw !== undefined) {
if (expr.raw === "\"use strict\"" || expr.raw === "'use strict'") {
return true;
}
} else {
if (expr.value === "use strict") {
return true;
}
}
}
}

That's per the ESTree spec.

ESLint doesn't set the directive option, so by default eslint-scope works with ESTree AST.

The conclusion is that it doesn't seem that __useDirective() is causing problems with ES3 described in this issue.

Off-topic, we could consider dropping the directive option and the DirectiveStatement branch in a future major version. We could also consider refactoring the other branch to use ESTree's directive property.

@mdjermanovic
Copy link
Member Author

isStrictScope() is the main function that calculates whether a given scope is strict by looking at its upper scope, nodes, directives, etc.

isStrictModeSupported() is a helper function added in estools/escope#93 only to check if the impliedStrict option value should be respected given the other options. It's currently used only here:

Program(node) {
this.scopeManager.__nestGlobalScope(node);
if (this.scopeManager.__isNodejsScope()) {
// Force strictness of GlobalScope to false when using node.js scope.
this.currentScope().isStrict = false;
this.scopeManager.__nestFunctionScope(node, false);
}
if (this.scopeManager.__isES6() && this.scopeManager.isModule()) {
this.scopeManager.__nestModuleScope(node);
}
if (this.scopeManager.isStrictModeSupported() && this.scopeManager.isImpliedStrict()) {
this.currentScope().isStrict = true;
}

I believe this issue will be fixed by setting this.isStrict = false if scopeManager.isStrictModeSupported() returns false, here:

eslint-scope/lib/scope.js

Lines 262 to 266 in ceb8bdd

/**
* Whether 'use strict' is in effect in this scope.
* @member {boolean} Scope#isStrict
*/
this.isStrict = isStrictScope(this, block, isMethodDefinition, scopeManager.__useDirective());

@mdjermanovic
Copy link
Member Author

PR #87

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Triage
Complete
Development

Successfully merging a pull request may close this issue.

2 participants