diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /tools/lint/eslint/eslint-plugin-mozilla/lib/rules/valid-lazy.js | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream/115.8.0esr.tar.xz firefox-esr-upstream/115.8.0esr.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/lint/eslint/eslint-plugin-mozilla/lib/rules/valid-lazy.js')
-rw-r--r-- | tools/lint/eslint/eslint-plugin-mozilla/lib/rules/valid-lazy.js | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/valid-lazy.js b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/valid-lazy.js new file mode 100644 index 0000000000..52ad4ce7d5 --- /dev/null +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/valid-lazy.js @@ -0,0 +1,221 @@ +/** + * @fileoverview Ensures that definitions and uses of properties on the + * ``lazy`` object are valid. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +"use strict"; +const helpers = require("../helpers"); + +const items = [ + "loader", + "XPCOMUtils", + "Integration", + "ChromeUtils", + "DevToolsUtils", + "Object", + "Reflect", +]; + +const callExpressionDefinitions = [ + /^loader\.lazyGetter\(lazy, "(\w+)"/, + /^loader\.lazyServiceGetter\(lazy, "(\w+)"/, + /^loader\.lazyRequireGetter\(lazy, "(\w+)"/, + /^XPCOMUtils\.defineLazyGetter\(lazy, "(\w+)"/, + /^Integration\.downloads\.defineESModuleGetter\(lazy, "(\w+)"/, + /^XPCOMUtils\.defineLazyModuleGetter\(lazy, "(\w+)"/, + /^ChromeUtils\.defineLazyGetter\(lazy, "(\w+)"/, + /^ChromeUtils\.defineModuleGetter\(lazy, "(\w+)"/, + /^XPCOMUtils\.defineLazyPreferenceGetter\(lazy, "(\w+)"/, + /^XPCOMUtils\.defineLazyProxy\(lazy, "(\w+)"/, + /^XPCOMUtils\.defineLazyScriptGetter\(lazy, "(\w+)"/, + /^XPCOMUtils\.defineLazyServiceGetter\(lazy, "(\w+)"/, + /^XPCOMUtils\.defineConstant\(lazy, "(\w+)"/, + /^DevToolsUtils\.defineLazyModuleGetter\(lazy, "(\w+)"/, + /^DevToolsUtils\.defineLazyGetter\(lazy, "(\w+)"/, + /^Object\.defineProperty\(lazy, "(\w+)"/, + /^Reflect\.defineProperty\(lazy, "(\w+)"/, +]; + +const callExpressionMultiDefinitions = [ + "ChromeUtils.defineESModuleGetters(lazy,", + "XPCOMUtils.defineLazyModuleGetters(lazy,", + "XPCOMUtils.defineLazyServiceGetters(lazy,", + "Object.defineProperties(lazy,", + "loader.lazyRequireGetter(lazy,", +]; + +module.exports = { + meta: { + docs: { + url: "https://firefox-source-docs.mozilla.org/code-quality/lint/linters/eslint-plugin-mozilla/valid-lazy.html", + }, + messages: { + duplicateSymbol: "Duplicate symbol {{name}} being added to lazy.", + 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.", + }, + schema: [], + type: "problem", + }, + + create(context) { + let lazyProperties = new Map(); + let unknownProperties = []; + + function addProp(node, name) { + if (lazyProperties.has(name)) { + context.report({ + node, + messageId: "duplicateSymbol", + data: { name }, + }); + return; + } + lazyProperties.set(name, { used: false, node }); + } + + function setPropertiesFromArgument(node, arg) { + if (arg.type === "ObjectExpression") { + for (let prop of arg.properties) { + if (prop.key.type == "Literal") { + context.report({ + node, + messageId: "incorrectType", + data: { name: prop.key.value }, + }); + continue; + } + addProp(node, prop.key.name); + } + } else if (arg.type === "ArrayExpression") { + for (let prop of arg.elements) { + if (prop.type != "Literal") { + continue; + } + addProp(node, prop.value); + } + } + } + + return { + VariableDeclarator(node) { + if ( + node.id.type === "Identifier" && + node.id.name == "lazy" && + node.init.type == "CallExpression" && + node.init.callee.name == "createLazyLoaders" + ) { + setPropertiesFromArgument(node, node.init.arguments[0]); + } + }, + + CallExpression(node) { + if ( + node.callee.type != "MemberExpression" || + (node.callee.object.type == "MemberExpression" && + !items.includes(node.callee.object.object.name)) || + (node.callee.object.type != "MemberExpression" && + !items.includes(node.callee.object.name)) + ) { + return; + } + + let source; + try { + source = helpers.getASTSource(node); + } catch (e) { + return; + } + + for (let reg of callExpressionDefinitions) { + let match = source.match(reg); + if (match) { + if (lazyProperties.has(match[1])) { + context.report({ + node, + messageId: "duplicateSymbol", + data: { name: match[1] }, + }); + return; + } + lazyProperties.set(match[1], { used: false, node }); + break; + } + } + + if ( + callExpressionMultiDefinitions.some(expr => + source.startsWith(expr) + ) && + node.arguments[1] + ) { + setPropertiesFromArgument(node, node.arguments[1]); + } + }, + + MemberExpression(node) { + if (node.computed || node.object.type !== "Identifier") { + return; + } + + let name; + if (node.object.name == "lazy") { + name = node.property.name; + } else { + return; + } + let property = lazyProperties.get(name); + if (!property) { + // These will be reported on Program:exit - some definitions may + // be after first use, so we need to wait until we've processed + // the whole file before reporting. + unknownProperties.push({ name, node }); + } else { + property.used = true; + } + if ( + helpers.getIsTopLevelAndUnconditionallyExecuted( + context.getAncestors() + ) + ) { + context.report({ + node, + messageId: "topLevelAndUnconditional", + data: { name }, + }); + } + }, + + "Program:exit": function () { + for (let { name, node } of unknownProperties) { + let property = lazyProperties.get(name); + if (!property) { + context.report({ + node, + messageId: "unknownProperty", + data: { name }, + }); + } else { + property.used = true; + } + } + for (let [name, property] of lazyProperties.entries()) { + if (!property.used) { + context.report({ + node: property.node, + messageId: "unusedProperty", + data: { name }, + }); + } + } + }, + }; + }, +}; |