block-scoped-var.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /**
  2. * @fileoverview Rule to check for "block scoped" variables by binding context
  3. * @author Matt DuVall <http://www.mattduvall.com>
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Rule Definition
  8. //------------------------------------------------------------------------------
  9. /** @type {import('../types').Rule.RuleModule} */
  10. module.exports = {
  11. meta: {
  12. type: "suggestion",
  13. docs: {
  14. description:
  15. "Enforce the use of variables within the scope they are defined",
  16. recommended: false,
  17. url: "https://eslint.org/docs/latest/rules/block-scoped-var",
  18. },
  19. schema: [],
  20. messages: {
  21. outOfScope:
  22. "'{{name}}' declared on line {{definitionLine}} column {{definitionColumn}} is used outside of binding context.",
  23. },
  24. },
  25. create(context) {
  26. let stack = [];
  27. const sourceCode = context.sourceCode;
  28. /**
  29. * Makes a block scope.
  30. * @param {ASTNode} node A node of a scope.
  31. * @returns {void}
  32. */
  33. function enterScope(node) {
  34. stack.push(node.range);
  35. }
  36. /**
  37. * Pops the last block scope.
  38. * @returns {void}
  39. */
  40. function exitScope() {
  41. stack.pop();
  42. }
  43. /**
  44. * Reports a given reference.
  45. * @param {eslint-scope.Reference} reference A reference to report.
  46. * @param {eslint-scope.Definition} definition A definition for which to report reference.
  47. * @returns {void}
  48. */
  49. function report(reference, definition) {
  50. const identifier = reference.identifier;
  51. const definitionPosition = definition.name.loc.start;
  52. context.report({
  53. node: identifier,
  54. messageId: "outOfScope",
  55. data: {
  56. name: identifier.name,
  57. definitionLine: definitionPosition.line,
  58. definitionColumn: definitionPosition.column + 1,
  59. },
  60. });
  61. }
  62. /**
  63. * Finds and reports references which are outside of valid scopes.
  64. * @param {ASTNode} node A node to get variables.
  65. * @returns {void}
  66. */
  67. function checkForVariables(node) {
  68. if (node.kind !== "var") {
  69. return;
  70. }
  71. // Defines a predicate to check whether or not a given reference is outside of valid scope.
  72. const scopeRange = stack.at(-1);
  73. /**
  74. * Check if a reference is out of scope
  75. * @param {ASTNode} reference node to examine
  76. * @returns {boolean} True is its outside the scope
  77. * @private
  78. */
  79. function isOutsideOfScope(reference) {
  80. const idRange = reference.identifier.range;
  81. return idRange[0] < scopeRange[0] || idRange[1] > scopeRange[1];
  82. }
  83. // Gets declared variables, and checks its references.
  84. const variables = sourceCode.getDeclaredVariables(node);
  85. for (let i = 0; i < variables.length; ++i) {
  86. // Reports.
  87. variables[i].references.filter(isOutsideOfScope).forEach(ref =>
  88. report(
  89. ref,
  90. variables[i].defs.find(def => def.parent === node),
  91. ),
  92. );
  93. }
  94. }
  95. return {
  96. Program(node) {
  97. stack = [node.range];
  98. },
  99. // Manages scopes.
  100. BlockStatement: enterScope,
  101. "BlockStatement:exit": exitScope,
  102. ForStatement: enterScope,
  103. "ForStatement:exit": exitScope,
  104. ForInStatement: enterScope,
  105. "ForInStatement:exit": exitScope,
  106. ForOfStatement: enterScope,
  107. "ForOfStatement:exit": exitScope,
  108. SwitchStatement: enterScope,
  109. "SwitchStatement:exit": exitScope,
  110. CatchClause: enterScope,
  111. "CatchClause:exit": exitScope,
  112. StaticBlock: enterScope,
  113. "StaticBlock:exit": exitScope,
  114. // Finds and reports references which are outside of valid scope.
  115. VariableDeclaration: checkForVariables,
  116. };
  117. },
  118. };