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