| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 | 
							- /**
 
-  * @fileoverview A rule to disallow using `this`/`super` before `super()`.
 
-  * @author Toru Nagashima
 
-  */
 
- "use strict";
 
- //------------------------------------------------------------------------------
 
- // Requirements
 
- //------------------------------------------------------------------------------
 
- const astUtils = require("./utils/ast-utils");
 
- //------------------------------------------------------------------------------
 
- // Helpers
 
- //------------------------------------------------------------------------------
 
- /**
 
-  * Checks whether or not a given node is a constructor.
 
-  * @param {ASTNode} node A node to check. This node type is one of
 
-  *   `Program`, `FunctionDeclaration`, `FunctionExpression`, and
 
-  *   `ArrowFunctionExpression`.
 
-  * @returns {boolean} `true` if the node is a constructor.
 
-  */
 
- function isConstructorFunction(node) {
 
-     return (
 
-         node.type === "FunctionExpression" &&
 
-         node.parent.type === "MethodDefinition" &&
 
-         node.parent.kind === "constructor"
 
-     );
 
- }
 
- //------------------------------------------------------------------------------
 
- // Rule Definition
 
- //------------------------------------------------------------------------------
 
- module.exports = {
 
-     meta: {
 
-         type: "problem",
 
-         docs: {
 
-             description: "disallow `this`/`super` before calling `super()` in constructors",
 
-             category: "ECMAScript 6",
 
-             recommended: true,
 
-             url: "https://eslint.org/docs/rules/no-this-before-super"
 
-         },
 
-         schema: [],
 
-         messages: {
 
-             noBeforeSuper: "'{{kind}}' is not allowed before 'super()'."
 
-         }
 
-     },
 
-     create(context) {
 
-         /*
 
-          * Information for each constructor.
 
-          * - upper:      Information of the upper constructor.
 
-          * - hasExtends: A flag which shows whether the owner class has a valid
 
-          *   `extends` part.
 
-          * - scope:      The scope of the owner class.
 
-          * - codePath:   The code path of this constructor.
 
-          */
 
-         let funcInfo = null;
 
-         /*
 
-          * Information for each code path segment.
 
-          * Each key is the id of a code path segment.
 
-          * Each value is an object:
 
-          * - superCalled:  The flag which shows `super()` called in all code paths.
 
-          * - invalidNodes: The array of invalid ThisExpression and Super nodes.
 
-          */
 
-         let segInfoMap = Object.create(null);
 
-         /**
 
-          * Gets whether or not `super()` is called in a given code path segment.
 
-          * @param {CodePathSegment} segment A code path segment to get.
 
-          * @returns {boolean} `true` if `super()` is called.
 
-          */
 
-         function isCalled(segment) {
 
-             return !segment.reachable || segInfoMap[segment.id].superCalled;
 
-         }
 
-         /**
 
-          * Checks whether or not this is in a constructor.
 
-          * @returns {boolean} `true` if this is in a constructor.
 
-          */
 
-         function isInConstructorOfDerivedClass() {
 
-             return Boolean(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends);
 
-         }
 
-         /**
 
-          * Checks whether or not this is before `super()` is called.
 
-          * @returns {boolean} `true` if this is before `super()` is called.
 
-          */
 
-         function isBeforeCallOfSuper() {
 
-             return (
 
-                 isInConstructorOfDerivedClass() &&
 
-                 !funcInfo.codePath.currentSegments.every(isCalled)
 
-             );
 
-         }
 
-         /**
 
-          * Sets a given node as invalid.
 
-          * @param {ASTNode} node A node to set as invalid. This is one of
 
-          *      a ThisExpression and a Super.
 
-          * @returns {void}
 
-          */
 
-         function setInvalid(node) {
 
-             const segments = funcInfo.codePath.currentSegments;
 
-             for (let i = 0; i < segments.length; ++i) {
 
-                 const segment = segments[i];
 
-                 if (segment.reachable) {
 
-                     segInfoMap[segment.id].invalidNodes.push(node);
 
-                 }
 
-             }
 
-         }
 
-         /**
 
-          * Sets the current segment as `super` was called.
 
-          * @returns {void}
 
-          */
 
-         function setSuperCalled() {
 
-             const segments = funcInfo.codePath.currentSegments;
 
-             for (let i = 0; i < segments.length; ++i) {
 
-                 const segment = segments[i];
 
-                 if (segment.reachable) {
 
-                     segInfoMap[segment.id].superCalled = true;
 
-                 }
 
-             }
 
-         }
 
-         return {
 
-             /**
 
-              * Adds information of a constructor into the stack.
 
-              * @param {CodePath} codePath A code path which was started.
 
-              * @param {ASTNode} node The current node.
 
-              * @returns {void}
 
-              */
 
-             onCodePathStart(codePath, node) {
 
-                 if (isConstructorFunction(node)) {
 
-                     // Class > ClassBody > MethodDefinition > FunctionExpression
 
-                     const classNode = node.parent.parent.parent;
 
-                     funcInfo = {
 
-                         upper: funcInfo,
 
-                         isConstructor: true,
 
-                         hasExtends: Boolean(
 
-                             classNode.superClass &&
 
-                             !astUtils.isNullOrUndefined(classNode.superClass)
 
-                         ),
 
-                         codePath
 
-                     };
 
-                 } else {
 
-                     funcInfo = {
 
-                         upper: funcInfo,
 
-                         isConstructor: false,
 
-                         hasExtends: false,
 
-                         codePath
 
-                     };
 
-                 }
 
-             },
 
-             /**
 
-              * Removes the top of stack item.
 
-              *
 
-              * And this traverses all segments of this code path then reports every
 
-              * invalid node.
 
-              * @param {CodePath} codePath A code path which was ended.
 
-              * @returns {void}
 
-              */
 
-             onCodePathEnd(codePath) {
 
-                 const isDerivedClass = funcInfo.hasExtends;
 
-                 funcInfo = funcInfo.upper;
 
-                 if (!isDerivedClass) {
 
-                     return;
 
-                 }
 
-                 codePath.traverseSegments((segment, controller) => {
 
-                     const info = segInfoMap[segment.id];
 
-                     for (let i = 0; i < info.invalidNodes.length; ++i) {
 
-                         const invalidNode = info.invalidNodes[i];
 
-                         context.report({
 
-                             messageId: "noBeforeSuper",
 
-                             node: invalidNode,
 
-                             data: {
 
-                                 kind: invalidNode.type === "Super" ? "super" : "this"
 
-                             }
 
-                         });
 
-                     }
 
-                     if (info.superCalled) {
 
-                         controller.skip();
 
-                     }
 
-                 });
 
-             },
 
-             /**
 
-              * Initialize information of a given code path segment.
 
-              * @param {CodePathSegment} segment A code path segment to initialize.
 
-              * @returns {void}
 
-              */
 
-             onCodePathSegmentStart(segment) {
 
-                 if (!isInConstructorOfDerivedClass()) {
 
-                     return;
 
-                 }
 
-                 // Initialize info.
 
-                 segInfoMap[segment.id] = {
 
-                     superCalled: (
 
-                         segment.prevSegments.length > 0 &&
 
-                         segment.prevSegments.every(isCalled)
 
-                     ),
 
-                     invalidNodes: []
 
-                 };
 
-             },
 
-             /**
 
-              * 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 (!isInConstructorOfDerivedClass()) {
 
-                     return;
 
-                 }
 
-                 // Update information inside of the loop.
 
-                 funcInfo.codePath.traverseSegments(
 
-                     { first: toSegment, last: fromSegment },
 
-                     (segment, controller) => {
 
-                         const info = segInfoMap[segment.id];
 
-                         if (info.superCalled) {
 
-                             info.invalidNodes = [];
 
-                             controller.skip();
 
-                         } else if (
 
-                             segment.prevSegments.length > 0 &&
 
-                             segment.prevSegments.every(isCalled)
 
-                         ) {
 
-                             info.superCalled = true;
 
-                             info.invalidNodes = [];
 
-                         }
 
-                     }
 
-                 );
 
-             },
 
-             /**
 
-              * Reports if this is before `super()`.
 
-              * @param {ASTNode} node A target node.
 
-              * @returns {void}
 
-              */
 
-             ThisExpression(node) {
 
-                 if (isBeforeCallOfSuper()) {
 
-                     setInvalid(node);
 
-                 }
 
-             },
 
-             /**
 
-              * Reports if this is before `super()`.
 
-              * @param {ASTNode} node A target node.
 
-              * @returns {void}
 
-              */
 
-             Super(node) {
 
-                 if (!astUtils.isCallee(node) && isBeforeCallOfSuper()) {
 
-                     setInvalid(node);
 
-                 }
 
-             },
 
-             /**
 
-              * Marks `super()` called.
 
-              * @param {ASTNode} node A target node.
 
-              * @returns {void}
 
-              */
 
-             "CallExpression:exit"(node) {
 
-                 if (node.callee.type === "Super" && isBeforeCallOfSuper()) {
 
-                     setSuperCalled();
 
-                 }
 
-             },
 
-             /**
 
-              * Resets state.
 
-              * @returns {void}
 
-              */
 
-             "Program:exit"() {
 
-                 segInfoMap = Object.create(null);
 
-             }
 
-         };
 
-     }
 
- };
 
 
  |