Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

promise-function-async: add options to specify function types to check. #3807

Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 69 additions & 17 deletions src/rules/promiseFunctionAsyncRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,32 @@ import { hasModifier } from "tsutils";
import * as ts from "typescript";
import * as Lint from "../index";

const OPTION_FUNCTION_DECLARATION = "check-function-declaration";
const OPTION_FUNCTION_EXPRESSION = "check-function-expression";
const OPTION_ARROW_FUNCTION = "check-arrow-function";
const OPTION_METHOD_DECLARATION = "check-method-declaration";

const KIND_FOR_OPTION: { [arg: string]: number } = {
[OPTION_FUNCTION_DECLARATION]: ts.SyntaxKind.FunctionDeclaration,
[OPTION_FUNCTION_EXPRESSION]: ts.SyntaxKind.FunctionExpression,
[OPTION_ARROW_FUNCTION]: ts.SyntaxKind.ArrowFunction,
[OPTION_METHOD_DECLARATION]: ts.SyntaxKind.MethodDeclaration,
};

type EnabledSyntaxKinds = ReadonlySet<number>;

function parseOptions(ruleArguments: string[]): EnabledSyntaxKinds {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think if this rule is enabled we should default to all kinds. Or else what's the point of turning the rule on in the first place?

if (ruleArguments.length === 0) {
ruleArguments = Object.keys(KIND_FOR_OPTION);
}

const enabledKinds = new Set<number>();
for (const arg of ruleArguments) {
enabledKinds.add(KIND_FOR_OPTION[arg]);
}
return enabledKinds;
}

export class Rule extends Lint.Rules.TypedRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
Expand All @@ -29,10 +55,32 @@ export class Rule extends Lint.Rules.TypedRule {
throwing an Error object. In contrast, non-\`async\` \`Promise\`-returning functions
are technically capable of either. This practice removes a requirement for consuming
code to handle both cases.

If no optional arguments are provided then all function types are checked,
otherwise the specific function types are checked:

* \`"${OPTION_FUNCTION_DECLARATION}"\` check function declarations.
* \`"${OPTION_FUNCTION_EXPRESSION}"\` check function expressions.
* \`"${OPTION_ARROW_FUNCTION}"\` check arrow functions.
* \`"${OPTION_METHOD_DECLARATION}"\` check method declarations.
`,
optionsDescription: "Not configurable.",
options: null,
optionExamples: [true],
options: {
type: "array",
items: {
type: "string",
enum: [
OPTION_FUNCTION_DECLARATION,
OPTION_FUNCTION_EXPRESSION,
OPTION_ARROW_FUNCTION,
OPTION_METHOD_DECLARATION,
],
},
minLength: 0,
maxLength: 4,
},
optionExamples: [true,
[true, OPTION_FUNCTION_DECLARATION, OPTION_METHOD_DECLARATION]],
type: "typescript",
typescriptOnly: false,
requiresTypeInfo: true,
Expand All @@ -42,24 +90,28 @@ export class Rule extends Lint.Rules.TypedRule {
public static FAILURE_STRING = "functions that return promises must be async";

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, parseOptions(this.ruleArguments), program.getTypeChecker());
}
}

function walk(ctx: Lint.WalkContext<void>, tc: ts.TypeChecker) {
return ts.forEachChild(ctx.sourceFile, function cb(node): void {
switch (node.kind) {
case ts.SyntaxKind.MethodDeclaration:
case ts.SyntaxKind.FunctionDeclaration:
if ((node as ts.FunctionLikeDeclaration).body === undefined) {
break;
}
// falls through
case ts.SyntaxKind.FunctionExpression:
case ts.SyntaxKind.ArrowFunction:
if (!hasModifier(node.modifiers, ts.SyntaxKind.AsyncKeyword) && returnsPromise(node as ts.FunctionLikeDeclaration, tc)) {
ctx.addFailure(node.getStart(ctx.sourceFile), (node as ts.FunctionLikeDeclaration).body!.pos, Rule.FAILURE_STRING);
}
function walk(ctx: Lint.WalkContext<EnabledSyntaxKinds>, tc: ts.TypeChecker) {
const { sourceFile, options } = ctx;
return ts.forEachChild(sourceFile, function cb(node): void {
if (options.has(node.kind)) {
switch (node.kind) {
case ts.SyntaxKind.MethodDeclaration:
case ts.SyntaxKind.FunctionDeclaration:
if ((node as ts.FunctionLikeDeclaration).body === undefined) {
break;
}
// falls through
case ts.SyntaxKind.FunctionExpression:
case ts.SyntaxKind.ArrowFunction:
if (!hasModifier(node.modifiers, ts.SyntaxKind.AsyncKeyword)
&& returnsPromise(node as ts.FunctionLikeDeclaration, tc)) {
ctx.addFailure(node.getStart(sourceFile), (node as ts.FunctionLikeDeclaration).body!.pos, Rule.FAILURE_STRING);
}
}
}
return ts.forEachChild(node, cb);
});
Expand Down