4 * Copyright 2017 Palantir Technologies, Inc.
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
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);
26 return _super !== null && _super.apply(this, arguments) || this;
28 Rule.prototype.apply = function (sourceFile) {
29 return this.applyWithFunction(sourceFile, walk);
31 /* tslint:disable:object-literal-sort-keys */
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.",
38 optionExamples: [true],
39 type: "functionality",
40 typescriptOnly: false,
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.";
46 }(Lint.Rules.AbstractRule));
49 return ts.forEachChild(ctx.sourceFile, function cb(node) {
50 if (tsutils_1.isConstructorDeclaration(node) && node.body !== undefined) {
51 getSuperForNode(node.body);
53 return ts.forEachChild(node, cb);
55 function getSuperForNode(node) {
56 if (tsutils_1.isIterationStatement(node)) {
57 var bodySuper = combineSequentialChildren(node);
58 if (typeof bodySuper === "number") {
59 return 0 /* NoSuper */;
61 if (!bodySuper.break) {
62 ctx.addFailureAtNode(bodySuper.node, Rule.FAILURE_STRING_LOOP);
64 return tslib_1.__assign({}, bodySuper, { break: false });
67 case ts.SyntaxKind.ReturnStatement:
68 case ts.SyntaxKind.ThrowStatement:
69 return 1 /* Return */;
70 case ts.SyntaxKind.BreakStatement:
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 &&
78 node.parent.expression === node
79 ? { node: node.parent, break: false }
81 case ts.SyntaxKind.ConditionalExpression: {
82 var _a = node, condition = _a.condition, whenTrue = _a.whenTrue, whenFalse = _a.whenFalse;
83 var inCondition = getSuperForNode(condition);
84 var inBranches = worse(getSuperForNode(whenTrue), getSuperForNode(whenFalse));
85 if (typeof inCondition !== "number" && typeof inBranches !== "number") {
86 addDuplicateFailure(inCondition.node, inBranches.node);
88 return worse(inCondition, inBranches);
90 case ts.SyntaxKind.IfStatement: {
91 var _b = node, thenStatement = _b.thenStatement, elseStatement = _b.elseStatement;
92 return worse(getSuperForNode(thenStatement), elseStatement !== undefined ? getSuperForNode(elseStatement) : 0 /* NoSuper */);
94 case ts.SyntaxKind.SwitchStatement:
95 return getSuperForSwitch(node);
97 return combineSequentialChildren(node);
100 function getSuperForSwitch(node) {
101 // 'super()' from any clause. Used to track whether 'super()' happens in the switch at all.
103 // 'super()' from the previous clause if it did not 'break;'.
104 var fallthroughSingle;
105 for (var _i = 0, _a = node.caseBlock.clauses; _i < _a.length; _i++) {
107 var clauseSuper = combineSequentialChildren(clause);
108 switch (clauseSuper) {
109 case 0 /* NoSuper */:
112 fallthroughSingle = undefined;
115 return 0 /* NoSuper */;
117 if (fallthroughSingle !== undefined) {
118 addDuplicateFailure(fallthroughSingle, clauseSuper.node);
120 if (!clauseSuper.break) {
121 fallthroughSingle = clauseSuper.node;
123 foundSingle = clauseSuper.node;
126 return foundSingle !== undefined ? { node: foundSingle, break: false } : 0 /* NoSuper */;
129 * Combines children that come one after another.
130 * (As opposed to if/else, switch, or loops, which need their own handling.)
132 function combineSequentialChildren(node) {
134 var res = ts.forEachChild(node, function (child) {
135 var childSuper = getSuperForNode(child);
136 switch (childSuper) {
137 case 0 /* NoSuper */:
140 if (seenSingle !== undefined) {
141 return tslib_1.__assign({}, seenSingle, { break: true });
147 if (seenSingle !== undefined && !seenSingle.break) {
148 addDuplicateFailure(seenSingle.node, childSuper.node);
150 seenSingle = childSuper;
154 return res !== undefined ? res : seenSingle !== undefined ? seenSingle : 0 /* NoSuper */;
156 function addDuplicateFailure(a, b) {
157 ctx.addFailure(a.getStart(), b.end, Rule.FAILURE_STRING_DUPLICATE);
160 // If/else run separately, so return the branch more likely to result in eventual errors.
161 function worse(a, b) {
162 return typeof a === "number"
163 ? typeof b === "number"
168 : typeof b === "number"