create(context) {
    const reported = new Set()
    const promiseCodePathContext = new PromiseCodePathContext()
    /**
     * @param {Identifier} node
     * @param {Identifier} resolved
     * @param {'certain' | 'potential'} kind
     */
    function report(node, resolved, kind) {
      if (reported.has(node)) {
        return
      }
      reported.add(node)
      context.report({
        node: node.parent,
        messageId:
          kind === 'certain' ? 'alreadyResolved' : 'potentiallyAlreadyResolved',
        data: {
          line: resolved.loc.start.line,
        },
      })
    }
    /**
     * @param {CodePathInfo} codePathInfo
     * @param {PromiseCodePathContext} promiseCodePathContext
     */
    function verifyMultipleResolvedPath(codePathInfo, promiseCodePathContext) {
      for (const { node, resolved, kind } of codePathInfo.iterateReports(
        promiseCodePathContext,
      )) {
        report(node, resolved, kind)
      }
    }

    /** @type {CodePathInfo[]} */
    const codePathInfoStack = []
    /** @type {Set<Identifier>[]} */
    const resolverReferencesStack = [new Set()]
    /** @type {ThrowableExpression | null} */
    let lastThrowableExpression = null
    return {
      /** @param {FunctionExpression | ArrowFunctionExpression} node */
      'FunctionExpression, ArrowFunctionExpression'(node) {
        if (!isPromiseConstructorWithInlineExecutor(node.parent)) {
          return
        }
        // Collect and stack `resolve` and `reject` references.
        /** @type {Set<Identifier>} */
        const resolverReferences = new Set()
        const resolvers = node.params.filter(
          /** @returns {node is Identifier} */
          (node) => node && node.type === 'Identifier',
        )
        for (const resolver of resolvers) {
          const variable = getScope(context, node).set.get(resolver.name)
          // istanbul ignore next -- Usually always present.
          if (!variable) continue
          for (const reference of variable.references) {
            resolverReferences.add(reference.identifier)
          }
        }

        resolverReferencesStack.unshift(resolverReferences)
      },
      /** @param {FunctionExpression | ArrowFunctionExpression} node */
      'FunctionExpression, ArrowFunctionExpression:exit'(node) {
        if (!isPromiseConstructorWithInlineExecutor(node.parent)) {
          return
        }
        resolverReferencesStack.shift()
      },
      /** @param {CodePath} path */
      onCodePathStart(path) {
        codePathInfoStack.unshift(new CodePathInfo(path))
      },
      onCodePathEnd() {
        const codePathInfo = codePathInfoStack.shift()
        if (codePathInfo.resolvedCount > 1) {
          verifyMultipleResolvedPath(codePathInfo, promiseCodePathContext)
        }
      },
      /** @param {ThrowableExpression} node */
      'CallExpression, MemberExpression, NewExpression, ImportExpression, YieldExpression:exit'(
        node,
      ) {
        lastThrowableExpression = node
      },
      /** @param {CodePathSegment} segment */
      onCodePathSegmentStart(segment) {
        codePathInfoStack[0].onSegmentEnter(segment)
      },
      /** @param {CodePathSegment} segment */
      /* istanbul ignore next */ // It is not called in ESLint v7.
      onUnreachableCodePathSegmentStart(segment) {
        codePathInfoStack[0].onSegmentEnter(segment)
      },
      /**
       * @param {CodePathSegment} segment
       * @param {Node} node
       */
      onCodePathSegmentEnd(segment, node) {
        if (
          node.type === 'CatchClause' &&
          lastThrowableExpression &&
          lastThrowableExpression.type === 'CallExpression' &&
          node.parent.type === 'TryStatement' &&
          node.parent.range[0] <= lastThrowableExpression.range[0] &&
          lastThrowableExpression.range[1] <= node.parent.range[1]
        ) {
          const resolverReferences = resolverReferencesStack[0]
          if (resolverReferences.has(lastThrowableExpression.callee)) {
            // Mark a segment if the last expression in the try block is a call to resolve.
            promiseCodePathContext.addResolvedTryBlockCodePathSegment(segment)
          }
        }
        codePathInfoStack[0].onSegmentExit(segment)
      },
      /** @param {CodePathSegment} segment */
      /* istanbul ignore next */ // It is not called in ESLint v7.
      onUnreachableCodePathSegmentEnd(segment) {
        codePathInfoStack[0].onSegmentExit(segment)
      },
      /** @type {Identifier} */
      'CallExpression > Identifier.callee'(node) {
        const codePathInfo = codePathInfoStack[0]
        const resolverReferences = resolverReferencesStack[0]
        if (!resolverReferences.has(node)) {
          return
        }
        for (const segmentInfo of codePathInfo.getCurrentSegmentInfos()) {
          // If a resolving path is found, report if the path is already resolved.
          // Store the information if it is not already resolved.
          if (segmentInfo.resolved) {
            report(node, segmentInfo.resolved, 'certain')
            continue
          }
          segmentInfo.resolved = node
        }
      },
    }
  }
@param {import('eslint').Rule.RuleContext} context