source: frontend/node_modules/tsutils/util/control-flow.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 13.1 KB
Line 
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.callExpressionAffectsControlFlow = exports.SignatureEffect = exports.getControlFlowEnd = exports.endsControlFlow = void 0;
4const ts = require("typescript");
5const node_1 = require("../typeguard/node");
6const util_1 = require("./util");
7function endsControlFlow(statement, checker) {
8 return getControlFlowEnd(statement, checker).end;
9}
10exports.endsControlFlow = endsControlFlow;
11const defaultControlFlowEnd = { statements: [], end: false };
12function getControlFlowEnd(statement, checker) {
13 return node_1.isBlockLike(statement) ? handleBlock(statement, checker) : getControlFlowEndWorker(statement, checker);
14}
15exports.getControlFlowEnd = getControlFlowEnd;
16function getControlFlowEndWorker(statement, checker) {
17 switch (statement.kind) {
18 case ts.SyntaxKind.ReturnStatement:
19 case ts.SyntaxKind.ThrowStatement:
20 case ts.SyntaxKind.ContinueStatement:
21 case ts.SyntaxKind.BreakStatement:
22 return { statements: [statement], end: true };
23 case ts.SyntaxKind.Block:
24 return handleBlock(statement, checker);
25 case ts.SyntaxKind.ForStatement:
26 case ts.SyntaxKind.WhileStatement:
27 return handleForAndWhileStatement(statement, checker);
28 case ts.SyntaxKind.ForOfStatement:
29 case ts.SyntaxKind.ForInStatement:
30 return handleForInOrOfStatement(statement, checker);
31 case ts.SyntaxKind.DoStatement:
32 return matchBreakOrContinue(getControlFlowEndWorker(statement.statement, checker), node_1.isBreakOrContinueStatement);
33 case ts.SyntaxKind.IfStatement:
34 return handleIfStatement(statement, checker);
35 case ts.SyntaxKind.SwitchStatement:
36 return matchBreakOrContinue(handleSwitchStatement(statement, checker), node_1.isBreakStatement);
37 case ts.SyntaxKind.TryStatement:
38 return handleTryStatement(statement, checker);
39 case ts.SyntaxKind.LabeledStatement:
40 return matchLabel(getControlFlowEndWorker(statement.statement, checker), statement.label);
41 case ts.SyntaxKind.WithStatement:
42 return getControlFlowEndWorker(statement.statement, checker);
43 case ts.SyntaxKind.ExpressionStatement:
44 if (checker === undefined)
45 return defaultControlFlowEnd;
46 return handleExpressionStatement(statement, checker);
47 default:
48 return defaultControlFlowEnd;
49 }
50}
51function handleBlock(statement, checker) {
52 const result = { statements: [], end: false };
53 for (const s of statement.statements) {
54 const current = getControlFlowEndWorker(s, checker);
55 result.statements.push(...current.statements);
56 if (current.end) {
57 result.end = true;
58 break;
59 }
60 }
61 return result;
62}
63function handleForInOrOfStatement(statement, checker) {
64 const end = matchBreakOrContinue(getControlFlowEndWorker(statement.statement, checker), node_1.isBreakOrContinueStatement);
65 end.end = false; // loop body is guaranteed to be executed
66 return end;
67}
68function handleForAndWhileStatement(statement, checker) {
69 const constantCondition = statement.kind === ts.SyntaxKind.WhileStatement
70 ? getConstantCondition(statement.expression)
71 : statement.condition === undefined || getConstantCondition(statement.condition);
72 if (constantCondition === false)
73 return defaultControlFlowEnd; // loop body is never executed
74 const end = matchBreakOrContinue(getControlFlowEndWorker(statement.statement, checker), node_1.isBreakOrContinueStatement);
75 if (constantCondition === undefined)
76 end.end = false; // can't be sure that loop body is executed at all
77 return end;
78}
79/** Simply detects `true` and `false` in conditions. That matches TypeScript's behavior. */
80function getConstantCondition(node) {
81 switch (node.kind) {
82 case ts.SyntaxKind.TrueKeyword:
83 return true;
84 case ts.SyntaxKind.FalseKeyword:
85 return false;
86 default:
87 return;
88 }
89}
90function handleIfStatement(node, checker) {
91 switch (getConstantCondition(node.expression)) {
92 case true:
93 // else branch is never executed
94 return getControlFlowEndWorker(node.thenStatement, checker);
95 case false:
96 // then branch is never executed
97 return node.elseStatement === undefined
98 ? defaultControlFlowEnd
99 : getControlFlowEndWorker(node.elseStatement, checker);
100 }
101 const then = getControlFlowEndWorker(node.thenStatement, checker);
102 if (node.elseStatement === undefined)
103 return {
104 statements: then.statements,
105 end: false,
106 };
107 const elze = getControlFlowEndWorker(node.elseStatement, checker);
108 return {
109 statements: [...then.statements, ...elze.statements],
110 end: then.end && elze.end,
111 };
112}
113function handleSwitchStatement(node, checker) {
114 let hasDefault = false;
115 const result = {
116 statements: [],
117 end: false,
118 };
119 for (const clause of node.caseBlock.clauses) {
120 if (clause.kind === ts.SyntaxKind.DefaultClause)
121 hasDefault = true;
122 const current = handleBlock(clause, checker);
123 result.end = current.end;
124 result.statements.push(...current.statements);
125 }
126 result.end && (result.end = hasDefault || checker !== undefined && util_1.hasExhaustiveCaseClauses(node, checker));
127 return result;
128}
129function handleTryStatement(node, checker) {
130 let finallyResult;
131 if (node.finallyBlock !== undefined) {
132 finallyResult = handleBlock(node.finallyBlock, checker);
133 // if 'finally' always ends control flow, we are not interested in any jump statements from 'try' or 'catch'
134 if (finallyResult.end)
135 return finallyResult;
136 }
137 const tryResult = handleBlock(node.tryBlock, checker);
138 if (node.catchClause === undefined)
139 return { statements: finallyResult.statements.concat(tryResult.statements), end: tryResult.end };
140 const catchResult = handleBlock(node.catchClause.block, checker);
141 return {
142 statements: tryResult.statements
143 // remove all throw statements and throwing function calls from the list of control flow statements inside tryBlock
144 .filter((s) => s.kind !== ts.SyntaxKind.ThrowStatement && s.kind !== ts.SyntaxKind.ExpressionStatement)
145 .concat(catchResult.statements, finallyResult === undefined ? [] : finallyResult.statements),
146 end: tryResult.end && catchResult.end, // only ends control flow if try AND catch definitely end control flow
147 };
148}
149/** Dotted name as TypeScript requires it for assertion signatures to affect control flow. */
150function isDottedNameWithExplicitTypeAnnotation(node, checker) {
151 while (true) {
152 switch (node.kind) {
153 case ts.SyntaxKind.Identifier: {
154 const symbol = checker.getExportSymbolOfSymbol(checker.getSymbolAtLocation(node));
155 return isExplicitlyTypedSymbol(util_1.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias) ? checker.getAliasedSymbol(symbol) : symbol, checker);
156 }
157 case ts.SyntaxKind.ThisKeyword:
158 return isExplicitlyTypedThis(node);
159 case ts.SyntaxKind.SuperKeyword:
160 return true;
161 case ts.SyntaxKind.PropertyAccessExpression:
162 if (!isExplicitlyTypedSymbol(checker.getSymbolAtLocation(node), checker))
163 return false;
164 // falls through
165 case ts.SyntaxKind.ParenthesizedExpression:
166 node = node.expression;
167 continue;
168 default:
169 return false;
170 }
171 }
172}
173function isExplicitlyTypedSymbol(symbol, checker) {
174 if (symbol === undefined)
175 return false;
176 if (util_1.isSymbolFlagSet(symbol, ts.SymbolFlags.Function | ts.SymbolFlags.Method | ts.SymbolFlags.Class | ts.SymbolFlags.ValueModule))
177 return true;
178 if (!util_1.isSymbolFlagSet(symbol, ts.SymbolFlags.Variable | ts.SymbolFlags.Property))
179 return false;
180 if (symbol.valueDeclaration === undefined)
181 return false;
182 if (declarationHasExplicitTypeAnnotation(symbol.valueDeclaration))
183 return true;
184 return node_1.isVariableDeclaration(symbol.valueDeclaration) &&
185 symbol.valueDeclaration.parent.parent.kind === ts.SyntaxKind.ForOfStatement &&
186 isDottedNameWithExplicitTypeAnnotation(symbol.valueDeclaration.parent.parent.expression, checker);
187}
188function declarationHasExplicitTypeAnnotation(node) {
189 if (ts.isJSDocPropertyLikeTag(node))
190 return node.typeExpression !== undefined;
191 return (node_1.isVariableDeclaration(node) ||
192 node_1.isParameterDeclaration(node) ||
193 node_1.isPropertyDeclaration(node) ||
194 node_1.isPropertySignature(node)) && (util_1.isNodeFlagSet(node, ts.NodeFlags.JavaScriptFile)
195 ? ts.getJSDocType(node)
196 : node.type) !== undefined;
197}
198function isExplicitlyTypedThis(node) {
199 var _a;
200 do {
201 node = node.parent;
202 if (node_1.isDecorator(node)) {
203 // `this` in decorators always resolves outside of the containing class
204 if (node.parent.kind === ts.SyntaxKind.Parameter && node_1.isClassLikeDeclaration(node.parent.parent.parent)) {
205 node = node.parent.parent.parent.parent;
206 }
207 else if (node_1.isClassLikeDeclaration(node.parent.parent)) {
208 node = node.parent.parent.parent;
209 }
210 else if (node_1.isClassLikeDeclaration(node.parent)) {
211 node = node.parent.parent;
212 }
213 }
214 } while (util_1.isFunctionScopeBoundary(node) !== 1 /* Function */ || node.kind === ts.SyntaxKind.ArrowFunction);
215 return util_1.isFunctionWithBody(node) &&
216 (util_1.isNodeFlagSet(node, ts.NodeFlags.JavaScriptFile)
217 ? ((_a = ts.getJSDocThisTag(node)) === null || _a === void 0 ? void 0 : _a.typeExpression) !== undefined
218 : node.parameters.length !== 0 && util_1.isThisParameter(node.parameters[0]) && node.parameters[0].type !== undefined) ||
219 node_1.isClassLikeDeclaration(node.parent);
220}
221var SignatureEffect;
222(function (SignatureEffect) {
223 SignatureEffect[SignatureEffect["Never"] = 1] = "Never";
224 SignatureEffect[SignatureEffect["Asserts"] = 2] = "Asserts";
225})(SignatureEffect = exports.SignatureEffect || (exports.SignatureEffect = {}));
226/**
227 * Dermines whether a top level CallExpression has a control flow effect according to TypeScript's rules.
228 * This handles functions returning `never` and `asserts`.
229 */
230function callExpressionAffectsControlFlow(node, checker) {
231 var _a, _b, _c;
232 if (!node_1.isExpressionStatement(node.parent) ||
233 ts.isOptionalChain(node) ||
234 !isDottedNameWithExplicitTypeAnnotation(node.expression, checker))
235 return;
236 const signature = checker.getResolvedSignature(node);
237 if ((signature === null || signature === void 0 ? void 0 : signature.declaration) === undefined)
238 return;
239 const typeNode = ts.isJSDocSignature(signature.declaration)
240 ? (_b = (_a = signature.declaration.type) === null || _a === void 0 ? void 0 : _a.typeExpression) === null || _b === void 0 ? void 0 : _b.type
241 : (_c = signature.declaration.type) !== null && _c !== void 0 ? _c : (util_1.isNodeFlagSet(signature.declaration, ts.NodeFlags.JavaScriptFile)
242 ? ts.getJSDocReturnType(signature.declaration)
243 : undefined);
244 if (typeNode === undefined)
245 return;
246 if (node_1.isTypePredicateNode(typeNode) && typeNode.assertsModifier !== undefined)
247 return 2 /* Asserts */;
248 return util_1.isTypeFlagSet(checker.getTypeFromTypeNode(typeNode), ts.TypeFlags.Never) ? 1 /* Never */ : undefined;
249}
250exports.callExpressionAffectsControlFlow = callExpressionAffectsControlFlow;
251function handleExpressionStatement(node, checker) {
252 if (!node_1.isCallExpression(node.expression))
253 return defaultControlFlowEnd;
254 switch (callExpressionAffectsControlFlow(node.expression, checker)) {
255 case 2 /* Asserts */:
256 return { statements: [node], end: false };
257 case 1 /* Never */:
258 return { statements: [node], end: true };
259 case undefined:
260 return defaultControlFlowEnd;
261 }
262}
263function matchBreakOrContinue(current, pred) {
264 const result = {
265 statements: [],
266 end: current.end,
267 };
268 for (const statement of current.statements) {
269 if (pred(statement) && statement.label === undefined) {
270 result.end = false;
271 continue;
272 }
273 result.statements.push(statement);
274 }
275 return result;
276}
277function matchLabel(current, label) {
278 const result = {
279 statements: [],
280 end: current.end,
281 };
282 const labelText = label.text;
283 for (const statement of current.statements) {
284 switch (statement.kind) {
285 case ts.SyntaxKind.BreakStatement:
286 case ts.SyntaxKind.ContinueStatement:
287 if (statement.label !== undefined && statement.label.text === labelText) {
288 result.end = false;
289 continue;
290 }
291 }
292 result.statements.push(statement);
293 }
294 return result;
295}
296//# sourceMappingURL=control-flow.js.map
Note: See TracBrowser for help on using the repository browser.