noDuplicateSuperRule.js 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. "use strict";
  2. /**
  3. * @license
  4. * Copyright 2017 Palantir Technologies, Inc.
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. Object.defineProperty(exports, "__esModule", { value: true });
  19. var tslib_1 = require("tslib");
  20. var tsutils_1 = require("tsutils");
  21. var ts = require("typescript");
  22. var Lint = require("../index");
  23. var Rule = /** @class */ (function (_super) {
  24. tslib_1.__extends(Rule, _super);
  25. function Rule() {
  26. return _super !== null && _super.apply(this, arguments) || this;
  27. }
  28. Rule.prototype.apply = function (sourceFile) {
  29. return this.applyWithFunction(sourceFile, walk);
  30. };
  31. /* tslint:disable:object-literal-sort-keys */
  32. Rule.metadata = {
  33. ruleName: "no-duplicate-super",
  34. description: "Warns if 'super()' appears twice in a constructor.",
  35. rationale: "The second call to 'super()' will fail at runtime.",
  36. optionsDescription: "Not configurable.",
  37. options: null,
  38. optionExamples: [true],
  39. type: "functionality",
  40. typescriptOnly: false,
  41. };
  42. /* tslint:enable:object-literal-sort-keys */
  43. Rule.FAILURE_STRING_DUPLICATE = "Multiple calls to 'super()' found. It must be called only once.";
  44. Rule.FAILURE_STRING_LOOP = "'super()' called in a loop. It must be called only once.";
  45. return Rule;
  46. }(Lint.Rules.AbstractRule));
  47. exports.Rule = Rule;
  48. function walk(ctx) {
  49. return ts.forEachChild(ctx.sourceFile, function cb(node) {
  50. if (tsutils_1.isConstructorDeclaration(node) && node.body !== undefined) {
  51. getSuperForNode(node.body);
  52. }
  53. return ts.forEachChild(node, cb);
  54. });
  55. function getSuperForNode(node) {
  56. if (tsutils_1.isIterationStatement(node)) {
  57. var bodySuper = combineSequentialChildren(node);
  58. if (typeof bodySuper === "number") {
  59. return 0 /* NoSuper */;
  60. }
  61. if (!bodySuper.break) {
  62. ctx.addFailureAtNode(bodySuper.node, Rule.FAILURE_STRING_LOOP);
  63. }
  64. return tslib_1.__assign({}, bodySuper, { break: false });
  65. }
  66. switch (node.kind) {
  67. case ts.SyntaxKind.ReturnStatement:
  68. case ts.SyntaxKind.ThrowStatement:
  69. return 1 /* Return */;
  70. case ts.SyntaxKind.BreakStatement:
  71. return 2 /* Break */;
  72. case ts.SyntaxKind.ClassDeclaration:
  73. case ts.SyntaxKind.ClassExpression:
  74. // 'super()' is bound differently inside, so ignore.
  75. return 0 /* NoSuper */;
  76. case ts.SyntaxKind.SuperKeyword:
  77. return node.parent.kind === ts.SyntaxKind.CallExpression && node.parent.expression === node
  78. ? { node: node.parent, break: false }
  79. : 0 /* NoSuper */;
  80. case ts.SyntaxKind.ConditionalExpression: {
  81. var _a = node, condition = _a.condition, whenTrue = _a.whenTrue, whenFalse = _a.whenFalse;
  82. var inCondition = getSuperForNode(condition);
  83. var inBranches = worse(getSuperForNode(whenTrue), getSuperForNode(whenFalse));
  84. if (typeof inCondition !== "number" && typeof inBranches !== "number") {
  85. addDuplicateFailure(inCondition.node, inBranches.node);
  86. }
  87. return worse(inCondition, inBranches);
  88. }
  89. case ts.SyntaxKind.IfStatement: {
  90. var _b = node, thenStatement = _b.thenStatement, elseStatement = _b.elseStatement;
  91. return worse(getSuperForNode(thenStatement), elseStatement !== undefined ? getSuperForNode(elseStatement) : 0 /* NoSuper */);
  92. }
  93. case ts.SyntaxKind.SwitchStatement:
  94. return getSuperForSwitch(node);
  95. default:
  96. return combineSequentialChildren(node);
  97. }
  98. }
  99. function getSuperForSwitch(node) {
  100. // 'super()' from any clause. Used to track whether 'super()' happens in the switch at all.
  101. var foundSingle;
  102. // 'super()' from the previous clause if it did not 'break;'.
  103. var fallthroughSingle;
  104. for (var _i = 0, _a = node.caseBlock.clauses; _i < _a.length; _i++) {
  105. var clause = _a[_i];
  106. var clauseSuper = combineSequentialChildren(clause);
  107. switch (clauseSuper) {
  108. case 0 /* NoSuper */:
  109. break;
  110. case 2 /* Break */:
  111. fallthroughSingle = undefined;
  112. break;
  113. case 1 /* Return */:
  114. return 0 /* NoSuper */;
  115. default:
  116. if (fallthroughSingle !== undefined) {
  117. addDuplicateFailure(fallthroughSingle, clauseSuper.node);
  118. }
  119. if (!clauseSuper.break) {
  120. fallthroughSingle = clauseSuper.node;
  121. }
  122. foundSingle = clauseSuper.node;
  123. }
  124. }
  125. return foundSingle !== undefined ? { node: foundSingle, break: false } : 0 /* NoSuper */;
  126. }
  127. /**
  128. * Combines children that come one after another.
  129. * (As opposed to if/else, switch, or loops, which need their own handling.)
  130. */
  131. function combineSequentialChildren(node) {
  132. var seenSingle;
  133. var res = ts.forEachChild(node, function (child) {
  134. var childSuper = getSuperForNode(child);
  135. switch (childSuper) {
  136. case 0 /* NoSuper */:
  137. return undefined;
  138. case 2 /* Break */:
  139. if (seenSingle !== undefined) {
  140. return tslib_1.__assign({}, seenSingle, { break: true });
  141. }
  142. return childSuper;
  143. case 1 /* Return */:
  144. return childSuper;
  145. default:
  146. if (seenSingle !== undefined && !seenSingle.break) {
  147. addDuplicateFailure(seenSingle.node, childSuper.node);
  148. }
  149. seenSingle = childSuper;
  150. return undefined;
  151. }
  152. });
  153. return res !== undefined ? res : seenSingle !== undefined ? seenSingle : 0 /* NoSuper */;
  154. }
  155. function addDuplicateFailure(a, b) {
  156. ctx.addFailure(a.getStart(), b.end, Rule.FAILURE_STRING_DUPLICATE);
  157. }
  158. }
  159. // If/else run separately, so return the branch more likely to result in eventual errors.
  160. function worse(a, b) {
  161. return typeof a === "number"
  162. ? typeof b === "number" ? (a < b ? b : a) : b
  163. : typeof b === "number" ? a : a.break ? b : a;
  164. }