forked from palantir/tslint
/
noAsyncWithoutAwaitRule.ts
99 lines (84 loc) · 3.51 KB
/
noAsyncWithoutAwaitRule.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
/**
* @license
* Copyright 2018 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as ts from "typescript";
import * as Lint from "../index";
import { codeExamples } from "./code-examples/noAsyncWithoutAwait.examples";
export class Rule extends Lint.Rules.AbstractRule {
public static metadata: Lint.IRuleMetadata = {
codeExamples,
description: "Do not write async functions that do not have an await statement in them",
hasFix: false,
optionExamples: [true],
options: null,
optionsDescription: "Not configurable.",
rationale: "Cleaner code, can possibly reduce transpiled output",
ruleName: "no-async-without-await",
type: "functionality",
typescriptOnly: false,
};
public static FAILURE_STRING = "Async functions with no await are not allowed.";
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new Walk(sourceFile, this.getOptions()));
}
}
class Walk extends Lint.RuleWalker {
protected visitFunctionDeclaration(node: ts.FunctionDeclaration) {
this.addFailureIfAsyncFunctionHasNoAwait(node);
super.visitFunctionDeclaration(node);
}
protected visitArrowFunction(node: ts.ArrowFunction) {
this.addFailureIfAsyncFunctionHasNoAwait(node);
super.visitArrowFunction(node);
}
protected visitMethodDeclaration(node: ts.MethodDeclaration) {
this.addFailureIfAsyncFunctionHasNoAwait(node);
super.visitMethodDeclaration(node);
}
private isAsyncFunction(node: ts.Node): boolean {
return Boolean(this.getAsyncModifier(node));
}
private getAsyncModifier(node: ts.Node) {
if (node.modifiers !== undefined) {
return node.modifiers.find((modifier) => modifier.kind === ts.SyntaxKind.AsyncKeyword);
}
return undefined;
}
private isAwait(node: ts.Node): boolean {
return node.kind === ts.SyntaxKind.AwaitKeyword;
}
private functionBlockHasAwait(node: ts.Node) {
if (this.isAwait(node)) {
return true;
}
// tslint:disable-next-line:no-unsafe-any
if (node.kind === ts.SyntaxKind.ArrowFunction
|| node.kind === ts.SyntaxKind.FunctionDeclaration) {
return false;
}
const awaitInChildren: boolean[] = node.getChildren()
.map((functionBlockNode: ts.Node) => this.functionBlockHasAwait(functionBlockNode));
return awaitInChildren.some(Boolean);
}
private addFailureIfAsyncFunctionHasNoAwait(node: ts.ArrowFunction | ts.FunctionDeclaration | ts.MethodDeclaration) {
if (this.isAsyncFunction(node) && !this.functionBlockHasAwait(node.body as ts.Node)) {
const asyncModifier = this.getAsyncModifier(node);
if (asyncModifier !== undefined) {
this.addFailureAt(asyncModifier.getStart(), asyncModifier.getEnd() - asyncModifier.getStart(), Rule.FAILURE_STRING);
}
}
}
}