Bug 1430923 - Handle EXPORTED_SYMBOLS in ESLint to help define globals in jsm files, and disallow 'let EXPORTED_SYMBOLS = '. r=mossop
authorMark Banner <standard8@mozilla.com>
Tue, 16 Jan 2018 23:07:25 +0000
changeset 451628 c1895745ba101970b61e9c938f8dd7f4ed73420a
parent 451627 d5548c012d2b243be36751bcc5f3c7e9f30bb429
child 451629 27d2fb339f71592bf934a5ae2182d1eb2b8555b9
push id8560
push userryanvm@gmail.com
push dateFri, 19 Jan 2018 16:34:00 +0000
treeherdermozilla-beta@4c9965a3b8a0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmossop
bugs1430923
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1430923 - Handle EXPORTED_SYMBOLS in ESLint to help define globals in jsm files, and disallow 'let EXPORTED_SYMBOLS = '. r=mossop MozReview-Commit-ID: 5abfpM8EAEL
tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
tools/lint/eslint/eslint-plugin-mozilla/lib/rules/mark-exported-symbols-as-used.js
tools/lint/eslint/eslint-plugin-mozilla/package-lock.json
tools/lint/eslint/eslint-plugin-mozilla/package.json
tools/lint/eslint/eslint-plugin-mozilla/tests/mark-exported-symbols-as-used.js
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js
@@ -63,17 +63,25 @@ module.exports = {
       "mozilla/use-services": "off"
     }
   }, {
     // Turn off browser env for all *.jsm files, and turn on the jsm environment.
     "env": {
       "browser": false,
       "mozilla/jsm": true
     },
-    "files": "**/*.jsm"
+    "files": "**/*.jsm",
+    "rules": {
+      "mozilla/mark-exported-symbols-as-used": "error"
+      // "no-unused-vars": ["error", {
+      //   "args": "none",
+      //   "vars": "all",
+      //   "varsIgnorePattern": "^Cc|Ci|Cu|Cr|EXPORTED_SYMBOLS"
+      // }]
+    }
   }],
 
   "parserOptions": {
     "ecmaFeatures": {
       "experimentalObjectRestSpread": true
     },
     "ecmaVersion": 8
   },
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/index.js
@@ -35,16 +35,17 @@ module.exports = {
     "avoid-removeChild": require("../lib/rules/avoid-removeChild"),
     "balanced-listeners": require("../lib/rules/balanced-listeners"),
     "import-browser-window-globals":
       require("../lib/rules/import-browser-window-globals"),
     "import-content-task-globals":
       require("../lib/rules/import-content-task-globals"),
     "import-globals": require("../lib/rules/import-globals"),
     "import-headjs-globals": require("../lib/rules/import-headjs-globals"),
+    "mark-exported-symbols-as-used": require("../lib/rules/mark-exported-symbols-as-used"),
     "mark-test-function-used": require("../lib/rules/mark-test-function-used"),
     "no-aArgs": require("../lib/rules/no-aArgs"),
     "no-arbitrary-setTimeout": require("../lib/rules/no-arbitrary-setTimeout"),
     "no-cpows-in-tests": require("../lib/rules/no-cpows-in-tests"),
     "no-single-arg-cu-import": require("../lib/rules/no-single-arg-cu-import"),
     "no-import-into-var-and-global":
       require("../lib/rules/no-import-into-var-and-global.js"),
     "no-task": require("../lib/rules/no-task"),
new file mode 100644
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/rules/mark-exported-symbols-as-used.js
@@ -0,0 +1,76 @@
+/**
+ * @fileoverview Simply marks exported symbols as used. Designed for use in
+ * .jsm files only.
+ *
+ * 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";
+
+function markArrayElementsAsUsed(context, node, expression) {
+  if (expression.type != "ArrayExpression") {
+    context.report({
+      node,
+      message: "Unexpected assignment of non-Array to EXPORTED_SYMBOLS"
+    });
+    return;
+  }
+
+  for (let element of expression.elements) {
+    context.markVariableAsUsed(element.value);
+  }
+}
+
+// -----------------------------------------------------------------------------
+// Rule Definition
+// -----------------------------------------------------------------------------
+
+module.exports = function(context) {
+  // Ignore assignments not in the global scope, e.g. where special module
+  // definitions are required due to having different ways of importing files,
+  // e.g. osfile.
+  function isGlobalScope() {
+    return !context.getScope().upper;
+  }
+
+  // ---------------------------------------------------------------------------
+  // Public
+  // ---------------------------------------------------------------------------
+
+  return {
+    AssignmentExpression(node, parents) {
+      if (node.operator === "=" &&
+          node.left.type === "MemberExpression" &&
+          node.left.object.type === "ThisExpression" &&
+          node.left.property.name === "EXPORTED_SYMBOLS" &&
+          isGlobalScope()) {
+        markArrayElementsAsUsed(context, node, node.right);
+      }
+    },
+
+    VariableDeclaration(node, parents) {
+      if (!isGlobalScope()) {
+        return;
+      }
+
+      for (let item of node.declarations) {
+        if (item.id &&
+            item.id.type == "Identifier" &&
+            item.id.name === "EXPORTED_SYMBOLS") {
+          if (node.kind === "let") {
+            // The use of 'let' isn't allowed as the lexical scope may die after
+            // the script executes.
+            context.report({
+              node,
+              message: "EXPORTED_SYMBOLS cannot be declared via `let`. Use `var` or `this.EXPORTED_SYMBOLS =`"
+            });
+          }
+
+          markArrayElementsAsUsed(context, node, item.init);
+        }
+      }
+    }
+  };
+};
--- a/tools/lint/eslint/eslint-plugin-mozilla/package-lock.json
+++ b/tools/lint/eslint/eslint-plugin-mozilla/package-lock.json
@@ -1,11 +1,11 @@
 {
   "name": "eslint-plugin-mozilla",
-  "version": "0.5.0",
+  "version": "0.6.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
     "acorn": {
       "version": "5.2.1",
       "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz",
       "integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w==",
       "dev": true
--- a/tools/lint/eslint/eslint-plugin-mozilla/package.json
+++ b/tools/lint/eslint/eslint-plugin-mozilla/package.json
@@ -1,11 +1,11 @@
 {
   "name": "eslint-plugin-mozilla",
-  "version": "0.5.0",
+  "version": "0.6.0",
   "description": "A collection of rules that help enforce JavaScript coding standard in the Mozilla project.",
   "keywords": [
     "eslint",
     "eslintplugin",
     "eslint-plugin",
     "mozilla",
     "firefox"
   ],
new file mode 100644
--- /dev/null
+++ b/tools/lint/eslint/eslint-plugin-mozilla/tests/mark-exported-symbols-as-used.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+var rule = require("../lib/rules/mark-exported-symbols-as-used");
+var RuleTester = require("eslint/lib/testers/rule-tester");
+
+const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+function invalidCode(code, type, message) {
+  return {code, errors: [{message, type}]};
+}
+
+ruleTester.run("mark-exported-symbols-as-used", rule, {
+  valid: [
+    "var EXPORTED_SYMBOLS = ['foo'];",
+    "this.EXPORTED_SYMBOLS = ['foo'];"
+  ],
+  invalid: [
+    invalidCode("let EXPORTED_SYMBOLS = ['foo'];", "VariableDeclaration",
+                "EXPORTED_SYMBOLS cannot be declared via `let`. Use `var` or `this.EXPORTED_SYMBOLS =`"),
+    invalidCode("var EXPORTED_SYMBOLS = 'foo';", "VariableDeclaration",
+                "Unexpected assignment of non-Array to EXPORTED_SYMBOLS"),
+    invalidCode("this.EXPORTED_SYMBOLS = 'foo';", "AssignmentExpression",
+                "Unexpected assignment of non-Array to EXPORTED_SYMBOLS")
+  ]
+});