text: /**
   * @fileoverview Rule to flag wrapping non-iife in parens
   * @author Gyandeep Singh
   */
  
  "use strict";
  
  //------------------------------------------------------------------------------
  // Requirements
  //------------------------------------------------------------------------------
  
  const astUtils = require("./utils/ast-utils");
  
  //------------------------------------------------------------------------------
  // Typedefs
  //------------------------------------------------------------------------------
  
  /**
   * Property name if it can be computed statically, otherwise the list of the tokens of the key node.
   * @typedef {string|Token[]} Key
   */
  
  /**
   * Accessor nodes with the same key.
   * @typedef {Object} AccessorData
   * @property {Key} key Accessor's key
   * @property {ASTNode[]} getters List of getter nodes.
   * @property {ASTNode[]} setters List of setter nodes.
   */
  
  //------------------------------------------------------------------------------
  // Helpers
  //------------------------------------------------------------------------------
  
  /**
   * Checks whether or not the given lists represent the equal tokens in the same order.
   * Tokens are compared by their properties, not by instance.
   * @param {Token[]} left First list of tokens.
   * @param {Token[]} right Second list of tokens.
   * @returns {boolean} `true` if the lists have same tokens.
   */
  function areEqualTokenLists(left, right) {
      if (left.length !== right.length) {
          return false;
      }
  
      for (let i = 0; i < left.length; i++) {
          const leftToken = left[i],
              rightToken = right[i];
  
          if (leftToken.type !== rightToken.type || leftToken.value !== rightToken.value) {
              return false;
          }
      }
  
      return true;
  }
  
  /**
   * Checks whether or not the given keys are equal.
   * @param {Key} left First key.
   * @param {Key} right Second key.
   * @returns {boolean} `true` if the keys are equal.
   */
  function areEqualKeys(left, right) {
      if (typeof left === "string" && typeof right === "string") {
  
          // Statically computed names.
          return left === right;
      }
      if (Array.isArray(left) && Array.isArray(right)) {
  
          // Token lists.
          return areEqualTokenLists(left, right);
      }
  
      return false;
  }
  
  /**
   * Checks whether or not a given node is of an accessor kind ('get' or 'set').
   * @param {ASTNode} node A node to check.
   * @returns {boolean} `true` if the node is of an accessor kind.
   */
  function isAccessorKind(node) {
      return node.kind === "get" || node.kind === "set";
  }
  
  /**
   * Checks whether or not a given node is an `Identifier` node which was named a given name.
   * @param {ASTNode} node A node to check.
   * @param {string} name An expected name of the node.
   * @returns {boolean} `true` if the node is an `Identifier` node which was named as expected.
   */
  function isIdentifier(node, name) {
      return node.type === "Identifier" && node.name === name;
  }
  
  /**
   * Checks whether or not a given node is an argument of a specified method call.
   * @param {ASTNode} node A node to check.
   * @param {number} index An expected index of the node in arguments.
   * @param {string} object An expected name of the object of the method.
   * @param {string} property An expected name of the method.
   * @returns {boolean} `true` if the node is an argument of the specified method call.
   */
  function isArgumentOfMethodCall(node, index, object, property) {
      const parent = node.parent;
  
      return (
          parent.type === "CallExpression" &&
          parent.callee.type === "MemberExpression" &&
          parent.callee.computed === false &&
          isIdentifier(parent.callee.object, object) &&
          isIdentifier(parent.callee.property, property) &&
          parent.arguments[index] === node
      );
  }
  
  /**
   * Checks whether or not a given node is a property descriptor.
   * @param {ASTNode} node A node to check.
   * @returns {boolean} `true` if the node is a property descriptor.
   */
  function isPropertyDescriptor(node) {
  
      // Object.defineProperty(obj, "foo", {set: ...})
      if (isArgumentOfMethodCall(node, 2, "Object", "defineProperty") ||
          isArgumentOfMethodCall(node, 2, "Reflect", "defineProperty")
      ) {
          return true;
      }
  
      /*
       * Object.defineProperties(obj, {foo: {set: ...}})
       * Object.create(proto, {foo: {set: ...}})
       */
      const grandparent = node.parent.parent;
  
      return grandparent.type === "ObjectExpression" && (
          isArgumentOfMethodCall(grandparent, 1, "Object", "create") ||
          isArgumentOfMethodCall(grandparent, 1, "Object", "defineProperties")
      );
  }
  
  //------------------------------------------------------------------------------
  // Rule Definition
  //------------------------------------------------------------------------------
  
  module.exports = {
      meta: {
          type: "suggestion",
  
          docs: {
              description: "enforce getter and setter pairs in objects and classes",
              category: "Best Practices",
              recommended: false,
              url: "https://eslint.org/docs/rules/accessor-pairs"
          },
  
          schema: [{
              type: "object",
              properties: {
                  getWithoutSet: {
                      type: "boolean",
                      default: false
                  },
                  setWithoutGet: {
                      type: "boolean",
                      default: true
                  },
                  enforceForClassMembers: {
                      type: "boolean",
                      default: false
                  }
              },
              additionalProperties: false
          }],
  
          messages: {
              missingGetterInPropertyDescriptor: "Getter is not present in property descriptor.",
              missingSetterInPropertyDescriptor: "Setter is not present in property descriptor.",
              missingGetterInObjectLiteral: "Getter is not present for {{ name }}.",
              missingSetterInObjectLiteral: "Setter is not present for {{ name }}.",
              missingGetterInClass: "Getter is not present for class {{ name }}.",
              missingSetterInClass: "Setter is not present for class {{ name }}."
          }
      },
      create(context) {
          const config = context.options[0] || {};
          const checkGetWithoutSet = config.getWithoutSet === true;
          const checkSetWithoutGet = config.setWithoutGet !== false;
          const enforceForClassMembers = config.enforceForClassMembers === true;
          const sourceCode = context.getSourceCode();
  
          /**
           * Reports the given node.
           * @param {ASTNode} node The node to report.
           * @param {string} messageKind "missingGetter" or "missingSetter".
           * @returns {void}
           * @private
           */
          function report(node, messageKind) {
              if (node.type === "Property") {
                  context.report({
                      node,
                      messageId: `${messageKind}InObjectLiteral`,
                      loc: astUtils.getFunctionHeadLoc(node.value, sourceCode),
                      data: { name: astUtils.getFunctionNameWithKind(node.value) }
                  });
              } else if (node.type === "MethodDefinition") {
                  context.report({
                      node,
                      messageId: `${messageKind}InClass`,
                      loc: astUtils.getFunctionHeadLoc(node.value, sourceCode),
                      data: { name: astUtils.getFunctionNameWithKind(node.value) }
                  });
              } else {
                  context.report({
                      node,
                      messageId: `${messageKind}InPropertyDescriptor`
                  });
              }
          }
  
          /**
           * Reports each of the nodes in the given list using the same messageId.
           * @param {ASTNode[]} nodes Nodes to report.
           * @param {string} messageKind "missingGetter" or "missingSetter".
           * @returns {void}
           * @private
           */
          function reportList(nodes, messageKind) {
              for (const node of nodes) {
                  report(node, messageKind);
              }
          }
  
          /**
           * Creates a new `AccessorData` object for the given getter or setter node.
           * @param {ASTNode} node A getter or setter node.
           * @returns {AccessorData} New `AccessorData` object that contains the given node.
           * @private
           */
          function createAccessorData(node) {
              const name = astUtils.getStaticPropertyName(node);
              const key = (name !== null) ? name : sourceCode.getTokens(node.key);
  
              return {
                  key,
                  getters: node.kind === "get" ? [node] : [],
                  setters: node.kind === "set" ? [node] : []
              };
          }
  
          /**
           * Merges the given `AccessorData` object into the given accessors list.
           * @param {AccessorData[]} accessors The list to merge into.
           * @param {AccessorData} accessorData The object to merge.
           * @returns {AccessorData[]} The same instance with the merged object.
           * @private
           */
          function mergeAccessorData(accessors, accessorData) {
              const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key));
  
              if (equalKeyElement) {
                  equalKeyElement.getters.push(...accessorData.getters);
                  equalKeyElement.setters.push(...accessorData.setters);
              } else {
                  accessors.push(accessorData);
              }
  
              return accessors;
          }
  
          /**
           * Checks accessor pairs in the given list of nodes.
           * @param {ASTNode[]} nodes The list to check.
           * @returns {void}
           * @private
           */
          function checkList(nodes) {
              const accessors = nodes
                  .filter(isAccessorKind)
                  .map(createAccessorData)
                  .reduce(mergeAccessorData, []);
  
              for (const { getters, setters } of accessors) {
                  if (checkSetWithoutGet && setters.length && !getters.length) {
                      reportList(setters, "missingGetter");
                  }
                  if (checkGetWithoutSet && getters.length && !setters.length) {
                      reportList(getters, "missingSetter");
                  }
              }
          }
  
          /**
           * Checks accessor pairs in an object literal.
           * @param {ASTNode} node `ObjectExpression` node to check.
           * @returns {void}
           * @private
           */
          function checkObjectLiteral(node) {
              checkList(node.properties.filter(p => p.type === "Property"));
          }
  
          /**
           * Checks accessor pairs in a property descriptor.
           * @param {ASTNode} node Property descriptor `ObjectExpression` node to check.
           * @returns {void}
           * @private
           */
          function checkPropertyDescriptor(node) {
              const namesToCheck = node.properties
                  .filter(p => p.type === "Property" && p.kind === "init" && !p.computed)
                  .map(({ key }) => key.name);
  
              const hasGetter = namesToCheck.includes("get");
              const hasSetter = namesToCheck.includes("set");
  
              if (checkSetWithoutGet && hasSetter && !hasGetter) {
                  report(node, "missingGetter");
              }
              if (checkGetWithoutSet && hasGetter && !hasSetter) {
                  report(node, "missingSetter");
              }
          }
  
          /**
           * Checks the given object expression as an object literal and as a possible property descriptor.
           * @param {ASTNode} node `ObjectExpression` node to check.
           * @returns {void}
           * @private
           */
          function checkObjectExpression(node) {
              checkObjectLiteral(node);
              if (isPropertyDescriptor(node)) {
                  checkPropertyDescriptor(node);
              }
          }
  
          /**
           * Checks the given class body.
           * @param {ASTNode} node `ClassBody` node to check.
           * @returns {void}
           * @private
           */
          function checkClassBody(node) {
              const methodDefinitions = node.body.filter(m => m.type === "MethodDefinition");
  
              checkList(methodDefinitions.filter(m => m.static));
              checkList(methodDefinitions.filter(m => !m.static));
          }
  
          const listeners = {};
  
          if (checkSetWithoutGet || checkGetWithoutSet) {
              listeners.ObjectExpression = checkObjectExpression;
              if (enforceForClassMembers) {
                  listeners.ClassBody = checkClassBody;
              }
          }
  
          return listeners;
      }
  };
  