no-shadow-restricted-names.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /**
  2. * @fileoverview Disallow shadowing of globalThis, NaN, undefined, and Infinity (ES2020 section 18.1)
  3. * @author Michael Ficarra
  4. */
  5. "use strict";
  6. /**
  7. * Determines if a variable safely shadows undefined.
  8. * This is the case when a variable named `undefined` is never assigned to a value (i.e. it always shares the same value
  9. * as the global).
  10. * @param {eslintScope.Variable} variable The variable to check
  11. * @returns {boolean} true if this variable safely shadows `undefined`
  12. */
  13. function safelyShadowsUndefined(variable) {
  14. return (
  15. variable.name === "undefined" &&
  16. variable.references.every(ref => !ref.isWrite()) &&
  17. variable.defs.every(
  18. def =>
  19. def.node.type === "VariableDeclarator" &&
  20. def.node.init === null,
  21. )
  22. );
  23. }
  24. //------------------------------------------------------------------------------
  25. // Rule Definition
  26. //------------------------------------------------------------------------------
  27. /** @type {import('../types').Rule.RuleModule} */
  28. module.exports = {
  29. meta: {
  30. type: "suggestion",
  31. defaultOptions: [
  32. {
  33. reportGlobalThis: false,
  34. },
  35. ],
  36. docs: {
  37. description: "Disallow identifiers from shadowing restricted names",
  38. recommended: true,
  39. url: "https://eslint.org/docs/latest/rules/no-shadow-restricted-names",
  40. },
  41. schema: [
  42. {
  43. type: "object",
  44. properties: {
  45. reportGlobalThis: {
  46. type: "boolean",
  47. },
  48. },
  49. additionalProperties: false,
  50. },
  51. ],
  52. messages: {
  53. shadowingRestrictedName: "Shadowing of global property '{{name}}'.",
  54. },
  55. },
  56. create(context) {
  57. const [{ reportGlobalThis }] = context.options;
  58. const RESTRICTED = new Set([
  59. "undefined",
  60. "NaN",
  61. "Infinity",
  62. "arguments",
  63. "eval",
  64. ]);
  65. if (reportGlobalThis) {
  66. RESTRICTED.add("globalThis");
  67. }
  68. const sourceCode = context.sourceCode;
  69. // Track reported nodes to avoid duplicate reports. For example, on class declarations.
  70. const reportedNodes = new Set();
  71. return {
  72. "VariableDeclaration, :function, CatchClause, ImportDeclaration, ClassDeclaration, ClassExpression"(
  73. node,
  74. ) {
  75. for (const variable of sourceCode.getDeclaredVariables(node)) {
  76. if (
  77. variable.defs.length > 0 &&
  78. RESTRICTED.has(variable.name) &&
  79. !safelyShadowsUndefined(variable)
  80. ) {
  81. for (const def of variable.defs) {
  82. const nodeToReport = def.name;
  83. if (!reportedNodes.has(nodeToReport)) {
  84. reportedNodes.add(nodeToReport);
  85. context.report({
  86. node: nodeToReport,
  87. messageId: "shadowingRestrictedName",
  88. data: {
  89. name: variable.name,
  90. },
  91. });
  92. }
  93. }
  94. }
  95. }
  96. },
  97. };
  98. },
  99. };