no-dupe-class-members.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  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. /** @type {import('../shared/types').Rule} */
  11. module.exports = {
  12. meta: {
  13. type: "problem",
  14. docs: {
  15. description: "Disallow duplicate class members",
  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, PropertyDefinition"(node) {
  61. const name = astUtils.getStaticPropertyName(node);
  62. const kind = node.type === "MethodDefinition" ? node.kind : "field";
  63. if (name === null || kind === "constructor") {
  64. return;
  65. }
  66. const state = getState(name, node.static);
  67. let isDuplicate = false;
  68. if (kind === "get") {
  69. isDuplicate = (state.init || state.get);
  70. state.get = true;
  71. } else if (kind === "set") {
  72. isDuplicate = (state.init || state.set);
  73. state.set = true;
  74. } else {
  75. isDuplicate = (state.init || state.get || state.set);
  76. state.init = true;
  77. }
  78. if (isDuplicate) {
  79. context.report({ node, messageId: "unexpected", data: { name } });
  80. }
  81. }
  82. };
  83. }
  84. };