block-scoped-var.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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. module.exports = {
  10. meta: {
  11. type: "suggestion",
  12. docs: {
  13. description: "enforce the use of variables within the scope they are defined",
  14. category: "Best Practices",
  15. recommended: false,
  16. url: "https://eslint.org/docs/rules/block-scoped-var"
  17. },
  18. schema: [],
  19. messages: {
  20. outOfScope: "'{{name}}' used outside of binding context."
  21. }
  22. },
  23. create(context) {
  24. let stack = [];
  25. /**
  26. * Makes a block scope.
  27. * @param {ASTNode} node A node of a scope.
  28. * @returns {void}
  29. */
  30. function enterScope(node) {
  31. stack.push(node.range);
  32. }
  33. /**
  34. * Pops the last block scope.
  35. * @returns {void}
  36. */
  37. function exitScope() {
  38. stack.pop();
  39. }
  40. /**
  41. * Reports a given reference.
  42. * @param {eslint-scope.Reference} reference A reference to report.
  43. * @returns {void}
  44. */
  45. function report(reference) {
  46. const identifier = reference.identifier;
  47. context.report({ node: identifier, messageId: "outOfScope", data: { name: identifier.name } });
  48. }
  49. /**
  50. * Finds and reports references which are outside of valid scopes.
  51. * @param {ASTNode} node A node to get variables.
  52. * @returns {void}
  53. */
  54. function checkForVariables(node) {
  55. if (node.kind !== "var") {
  56. return;
  57. }
  58. // Defines a predicate to check whether or not a given reference is outside of valid scope.
  59. const scopeRange = stack[stack.length - 1];
  60. /**
  61. * Check if a reference is out of scope
  62. * @param {ASTNode} reference node to examine
  63. * @returns {boolean} True is its outside the scope
  64. * @private
  65. */
  66. function isOutsideOfScope(reference) {
  67. const idRange = reference.identifier.range;
  68. return idRange[0] < scopeRange[0] || idRange[1] > scopeRange[1];
  69. }
  70. // Gets declared variables, and checks its references.
  71. const variables = context.getDeclaredVariables(node);
  72. for (let i = 0; i < variables.length; ++i) {
  73. // Reports.
  74. variables[i]
  75. .references
  76. .filter(isOutsideOfScope)
  77. .forEach(report);
  78. }
  79. }
  80. return {
  81. Program(node) {
  82. stack = [node.range];
  83. },
  84. // Manages scopes.
  85. BlockStatement: enterScope,
  86. "BlockStatement:exit": exitScope,
  87. ForStatement: enterScope,
  88. "ForStatement:exit": exitScope,
  89. ForInStatement: enterScope,
  90. "ForInStatement:exit": exitScope,
  91. ForOfStatement: enterScope,
  92. "ForOfStatement:exit": exitScope,
  93. SwitchStatement: enterScope,
  94. "SwitchStatement:exit": exitScope,
  95. CatchClause: enterScope,
  96. "CatchClause:exit": exitScope,
  97. // Finds and reports references which are outside of valid scope.
  98. VariableDeclaration: checkForVariables
  99. };
  100. }
  101. };