Bug 1773747 - Part 6: Add ESLint rule to check immediately-used lazy getter. r=Standard8

Differential Revision: https://phabricator.services.mozilla.com/D149867
This commit is contained in:
Tooru Fujisawa 2022-06-24 11:03:53 +00:00
Родитель 55a0bf7709
Коммит ab853db23f
4 изменённых файлов: 152 добавлений и 18 удалений

Просмотреть файл

@ -2,8 +2,8 @@ valid-lazy
==========
Ensures that definitions and uses of properties on the ``lazy`` object are valid.
This rule checks for using unknown properties, duplicated symbols and unused
symbols.
This rule checks for using unknown properties, duplicated symbols, unused
symbols, and also lazy getter used at top-level unconditionally.
Examples of incorrect code for this rule:
-----------------------------------------
@ -11,8 +11,10 @@ Examples of incorrect code for this rule:
.. code-block:: js
const lazy = {};
// Unknown lazy member property {{name}}
lazy.bar.foo();
if (x) {
// Unknown lazy member property {{name}}
lazy.bar.foo();
}
.. code-block:: js
@ -21,7 +23,9 @@ Examples of incorrect code for this rule:
// Duplicate symbol foo being added to lazy.
XPCOMUtils.defineLazyGetter(lazy, "foo", "foo1.jsm");
lazy.foo3.bar();
if (x) {
lazy.foo3.bar();
}
.. code-block:: js
@ -29,6 +33,13 @@ Examples of incorrect code for this rule:
// Unused lazy property foo
XPCOMUtils.defineLazyGetter(lazy, "foo", "foo.jsm");
.. code-block:: js
const lazy = {};
XPCOMUtils.defineLazyGetter(lazy, "foo", "foo.jsm");
// Used at top-level unconditionally.
lazy.foo.bar();
Examples of correct code for this rule:
---------------------------------------
@ -38,5 +49,7 @@ Examples of correct code for this rule:
XPCOMUtils.defineLazyGetter(lazy, "foo1", () => {});
XPCOMUtils.defineLazyModuleGetters(lazy, { foo2: "foo2.jsm" });
lazy.foo1.bar();
lazy.foo2.bar();
if (x) {
lazy.foo1.bar();
lazy.foo2.bar();
}

Просмотреть файл

@ -533,6 +533,61 @@ module.exports = {
return true;
},
/**
* Check whether the node is evaluated at top-level script unconditionally.
*
* @param {Array} ancestors
* The parents of the current node.
*
* @return {Boolean}
* True or false
*/
getIsTopLevelAndUnconditionallyExecuted(ancestors) {
for (let parent of ancestors) {
switch (parent.type) {
// Control flow
case "IfStatement":
case "SwitchStatement":
case "TryStatement":
case "WhileStatement":
case "DoWhileStatement":
case "ForStatement":
case "ForInStatement":
case "ForOfStatement":
return false;
// Function
case "FunctionDeclaration":
case "FunctionExpression":
case "ArrowFunctionExpression":
case "ClassBody":
return false;
// Branch
case "LogicalExpression":
case "ConditionalExpression":
case "ChainExpression":
return false;
case "AssignmentExpression":
switch (parent.operator) {
// Branch
case "||=":
case "&&=":
case "??=":
return false;
}
break;
// Implicit branch (default value)
case "ObjectPattern":
case "ArrayPattern":
return false;
}
}
return true;
},
/**
* Check whether we might be in a test head file.
*

Просмотреть файл

@ -59,6 +59,8 @@ module.exports = {
incorrectType: "Unexpected literal for property name {{name}}",
unknownProperty: "Unknown lazy member property {{name}}",
unusedProperty: "Unused lazy property {{name}}",
topLevelAndUnconditional:
"Lazy property {{name}} is used at top-level unconditionally. It should be non-lazy.",
},
type: "problem",
},
@ -178,6 +180,17 @@ module.exports = {
} else {
property.used = true;
}
if (
helpers.getIsTopLevelAndUnconditionallyExecuted(
context.getAncestors()
)
) {
context.report({
node,
messageId: "topLevelAndUnconditional",
data: { name },
});
}
},
"Program:exit": function() {

Просмотреть файл

@ -10,7 +10,7 @@
var rule = require("../lib/rules/valid-lazy");
var RuleTester = require("eslint").RuleTester;
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 13 } });
// ------------------------------------------------------------------------------
// Tests
@ -27,30 +27,30 @@ ruleTester.run("valid-lazy", rule, {
`
const lazy = {};
XPCOMUtils.defineLazyGetter(lazy, "foo", () => {});
lazy.foo.bar();
if (x) { lazy.foo.bar(); }
`,
`
const lazy = {};
XPCOMUtils.defineLazyModuleGetters(lazy, {
foo: "foo.jsm",
});
lazy.foo.bar();
if (x) { lazy.foo.bar(); }
`,
`
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
foo: "foo.mjs",
});
lazy.foo.bar();
if (x) { lazy.foo.bar(); }
`,
`
const lazy = {};
Integration.downloads.defineModuleGetter(lazy, "foo", "foo.jsm");
lazy.foo.bar();
if (x) { lazy.foo.bar(); }
`,
`
const lazy = createLazyLoaders({ foo: () => {}});
lazy.foo.bar();
if (x) { lazy.foo.bar(); }
`,
`
const lazy = {};
@ -60,18 +60,57 @@ ruleTester.run("valid-lazy", rule, {
"bar",
true
);
lazy.foo1.bar();
lazy.foo2.bar();
if (x) {
lazy.foo1.bar();
lazy.foo2.bar();
}
`,
// Test for top-level unconditional.
`
const lazy = {};
XPCOMUtils.defineLazyGetter(lazy, "foo", () => {});
if (x) { lazy.foo.bar(); }
for (;;) { lazy.foo.bar(); }
for (var x in y) { lazy.foo.bar(); }
for (var x of y) { lazy.foo.bar(); }
while (true) { lazy.foo.bar(); }
do { lazy.foo.bar(); } while (true);
switch (x) { case 1: lazy.foo.bar(); }
try { lazy.foo.bar(); } catch (e) {}
function f() { lazy.foo.bar(); }
(function f() { lazy.foo.bar(); });
() => { lazy.foo.bar(); };
class C {
constructor() { lazy.foo.bar(); }
foo() { lazy.foo.bar(); }
get x() { lazy.foo.bar(); }
set x(v) { lazy.foo.bar(); }
a = lazy.foo.bar();
#b = lazy.foo.bar();
static {
lazy.foo.bar();
}
}
a && lazy.foo.bar();
a || lazy.foo.bar();
a ?? lazy.foo.bar();
a ? lazy.foo.bar() : b;
a?.b[lazy.foo.bar()];
a ||= lazy.foo.bar();
a &&= lazy.foo.bar();
a ??= lazy.foo.bar();
var { x = lazy.foo.bar() } = {};
var [ y = lazy.foo.bar() ] = [];
`,
],
invalid: [
invalidCode("lazy.bar", "bar", "unknownProperty"),
invalidCode("if (x) { lazy.bar; }", "bar", "unknownProperty"),
invalidCode(
`
const lazy = {};
XPCOMUtils.defineLazyGetter(lazy, "foo", "foo.jsm");
XPCOMUtils.defineLazyGetter(lazy, "foo", "foo1.jsm");
lazy.foo.bar();
if (x) { lazy.foo.bar(); }
`,
"foo",
"duplicateSymbol"
@ -82,7 +121,7 @@ ruleTester.run("valid-lazy", rule, {
XPCOMUtils.defineLazyModuleGetters(lazy, {
"foo-bar": "foo.jsm",
});
lazy["foo-bar"].bar();
if (x) { lazy["foo-bar"].bar(); }
`,
"foo-bar",
"incorrectType"
@ -94,5 +133,19 @@ ruleTester.run("valid-lazy", rule, {
"foo",
"unusedProperty"
),
invalidCode(
`const lazy = {};
XPCOMUtils.defineLazyGetter(lazy, "foo1", () => {});
lazy.foo1.bar();`,
"foo1",
"topLevelAndUnconditional"
),
invalidCode(
`const lazy = {};
XPCOMUtils.defineLazyGetter(lazy, "foo1", () => {});
{ x = -f(1 + lazy.foo1.bar()); }`,
"foo1",
"topLevelAndUnconditional"
),
],
});