Loading docs/code-quality/lint/linters/eslint-plugin-mozilla/valid-lazy.rst +20 −7 Original line number Diff line number Diff line Loading @@ -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: ----------------------------------------- Loading @@ -11,8 +11,10 @@ Examples of incorrect code for this rule: .. code-block:: js const lazy = {}; if (x) { // Unknown lazy member property {{name}} lazy.bar.foo(); } .. code-block:: js Loading @@ -21,7 +23,9 @@ Examples of incorrect code for this rule: // Duplicate symbol foo being added to lazy. XPCOMUtils.defineLazyGetter(lazy, "foo", "foo1.jsm"); if (x) { lazy.foo3.bar(); } .. code-block:: js Loading @@ -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: --------------------------------------- Loading @@ -38,5 +49,7 @@ Examples of correct code for this rule: XPCOMUtils.defineLazyGetter(lazy, "foo1", () => {}); XPCOMUtils.defineLazyModuleGetters(lazy, { foo2: "foo2.jsm" }); if (x) { lazy.foo1.bar(); lazy.foo2.bar(); } tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js +55 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading tools/lint/eslint/eslint-plugin-mozilla/lib/rules/valid-lazy.js +13 −0 Original line number Diff line number Diff line Loading @@ -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", }, Loading Loading @@ -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() { Loading tools/lint/eslint/eslint-plugin-mozilla/tests/valid-lazy.js +64 −11 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 = {}; Loading @@ -60,18 +60,57 @@ ruleTester.run("valid-lazy", rule, { "bar", true ); 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" Loading @@ -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" Loading @@ -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" ), ], }); Loading
docs/code-quality/lint/linters/eslint-plugin-mozilla/valid-lazy.rst +20 −7 Original line number Diff line number Diff line Loading @@ -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: ----------------------------------------- Loading @@ -11,8 +11,10 @@ Examples of incorrect code for this rule: .. code-block:: js const lazy = {}; if (x) { // Unknown lazy member property {{name}} lazy.bar.foo(); } .. code-block:: js Loading @@ -21,7 +23,9 @@ Examples of incorrect code for this rule: // Duplicate symbol foo being added to lazy. XPCOMUtils.defineLazyGetter(lazy, "foo", "foo1.jsm"); if (x) { lazy.foo3.bar(); } .. code-block:: js Loading @@ -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: --------------------------------------- Loading @@ -38,5 +49,7 @@ Examples of correct code for this rule: XPCOMUtils.defineLazyGetter(lazy, "foo1", () => {}); XPCOMUtils.defineLazyModuleGetters(lazy, { foo2: "foo2.jsm" }); if (x) { lazy.foo1.bar(); lazy.foo2.bar(); }
tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js +55 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading
tools/lint/eslint/eslint-plugin-mozilla/lib/rules/valid-lazy.js +13 −0 Original line number Diff line number Diff line Loading @@ -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", }, Loading Loading @@ -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() { Loading
tools/lint/eslint/eslint-plugin-mozilla/tests/valid-lazy.js +64 −11 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 = {}; Loading @@ -60,18 +60,57 @@ ruleTester.run("valid-lazy", rule, { "bar", true ); 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" Loading @@ -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" Loading @@ -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" ), ], });