no-dupe-class-members.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. /**
  2. * @fileoverview A rule to disallow duplicate name in class members.
  3. * @author Toru Nagashima
  4. */
  5. "use strict";
  6. const astUtils = require("./utils/ast-utils");
  7. //------------------------------------------------------------------------------
  8. // Rule Definition
  9. //------------------------------------------------------------------------------
  10. module.exports = {
  11. meta: {
  12. type: "problem",
  13. docs: {
  14. description: "disallow duplicate class members",
  15. category: "ECMAScript 6",
  16. recommended: true,
  17. url: "https://eslint.org/docs/rules/no-dupe-class-members"
  18. },
  19. schema: [],
  20. messages: {
  21. unexpected: "Duplicate name '{{name}}'."
  22. }
  23. },
  24. create(context) {
  25. let stack = [];
  26. /**
  27. * Gets state of a given member name.
  28. * @param {string} name A name of a member.
  29. * @param {boolean} isStatic A flag which specifies that is a static member.
  30. * @returns {Object} A state of a given member name.
  31. * - retv.init {boolean} A flag which shows the name is declared as normal member.
  32. * - retv.get {boolean} A flag which shows the name is declared as getter.
  33. * - retv.set {boolean} A flag which shows the name is declared as setter.
  34. */
  35. function getState(name, isStatic) {
  36. const stateMap = stack[stack.length - 1];
  37. const key = `$${name}`; // to avoid "__proto__".
  38. if (!stateMap[key]) {
  39. stateMap[key] = {
  40. nonStatic: { init: false, get: false, set: false },
  41. static: { init: false, get: false, set: false }
  42. };
  43. }
  44. return stateMap[key][isStatic ? "static" : "nonStatic"];
  45. }
  46. return {
  47. // Initializes the stack of state of member declarations.
  48. Program() {
  49. stack = [];
  50. },
  51. // Initializes state of member declarations for the class.
  52. ClassBody() {
  53. stack.push(Object.create(null));
  54. },
  55. // Disposes the state for the class.
  56. "ClassBody:exit"() {
  57. stack.pop();
  58. },
  59. // Reports the node if its name has been declared already.
  60. MethodDefinition(node) {
  61. const name = astUtils.getStaticPropertyName(node);
  62. if (name === null || node.kind === "constructor") {
  63. return;
  64. }
  65. const state = getState(name, node.static);
  66. let isDuplicate = false;
  67. if (node.kind === "get") {
  68. isDuplicate = (state.init || state.get);
  69. state.get = true;
  70. } else if (node.kind === "set") {
  71. isDuplicate = (state.init || state.set);
  72. state.set = true;
  73. } else {
  74. isDuplicate = (state.init || state.get || state.set);
  75. state.init = true;
  76. }
  77. if (isDuplicate) {
  78. context.report({ node, messageId: "unexpected", data: { name } });
  79. }
  80. }
  81. };
  82. }
  83. };