Repository: eslint/eslint
File: lib/rules/constructor-super.js (JavaScript)

Code before:

            onUnreachableCodePathSegmentEnd(segment) {
                funcInfo.currentSegments.delete(segment);
            },

            onCodePathSegmentEnd(segment) {
                funcInfo.currentSegments.delete(segment);
            },


            /**
             * Update information of the code path segment when a code path was
             * looped.
             * @param {CodePathSegment} fromSegment The code path segment of the
             *      end of a loop.
             * @param {CodePathSegment} toSegment A code path segment of the head
             *      of a loop.
             * @returns {void}
             */
            onCodePathSegmentLoop(fromSegment, toSegment) {
                if (!(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends)) {
                    return;
                }

                // Update information inside of the loop.
                const isRealLoop = toSegment.prevSegments.length >= 2;

                funcInfo.codePath.traverseSegments(
                    { first: toSegment, last: fromSegment },
                    segment => {
                        const info = segInfoMap[segment.id] ?? new SegmentInfo();
                        const seenPrevSegments = segment.prevSegments.filter(hasSegmentBeenSeen);

                        // Updates flags.
                        info.calledInSomePaths = seenPrevSegments.some(isCalledInSomePath);
                        info.calledInEveryPaths = seenPrevSegments.every(isCalledInEveryPath);

                        // If flags become true anew, reports the valid nodes.
                        if (info.calledInSomePaths || isRealLoop) {
                            const nodes = info.validNodes;

                            info.validNodes = [];

                            for (let i = 0; i < nodes.length; ++i) {
                                const node = nodes[i];

                                context.report({
                                    messageId: "duplicate",
                                    node
                                });
                            }

Review comment (suggestion):
This code that updates segments in loops was causing false positives. It didn't account for the case that the segment could have had a `super()` call inside the segment itself.

Code after:
             */
            onCodePathSegmentLoop(fromSegment, toSegment) {
                if (!(funcInfo.isConstructor && funcInfo.hasExtends)) {
                    return;
                }

                funcInfo.codePath.traverseSegments(
                    { first: toSegment, last: fromSegment },
                    (segment, controller) => {
                        const info = segInfoMap[segment.id];

                        // skip segments after the loop
                        if (!info) {
                            controller.skip();
                            return;
                        }

                        const seenPrevSegments = segment.prevSegments.filter(hasSegmentBeenSeen);
                        const calledInSomePreviousPaths = seenPrevSegments.some(isCalledInSomePath);
                        const calledInEveryPreviousPaths = seenPrevSegments.every(isCalledInEveryPath);

                        info.calledInSomePaths ||= calledInSomePreviousPaths;
                        info.calledInEveryPaths ||= calledInEveryPreviousPaths;

                        // If flags become true anew, reports the valid nodes.
                        if (calledInSomePreviousPaths) {
                            const nodes = info.validNodes;

                            info.validNodes = [];

                            for (let i = 0; i < nodes.length; ++i) {
                                const node = nodes[i];

                                context.report({
                                    messageId: "duplicate",
                                    node
                                });
                            }
                        }
                    }
                );
            },

            /**
             * Checks for a call of `super()`.
             * @param {ASTNode} node A CallExpression node to check.
             * @returns {void}
             */
            "CallExpression:exit"(node) {
                if (!(funcInfo.isConstructor && funcInfo.hasExtends)) {
                    return;