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

Commit

Permalink
Add await-promise rule.
Browse files Browse the repository at this point in the history
  • Loading branch information
andy-hanson committed Jan 22, 2017
1 parent 8207eff commit 05e6041
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 0 deletions.
74 changes: 74 additions & 0 deletions src/rules/awaitPromiseRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* @license
* Copyright 2017 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";

export class Rule extends Lint.Rules.TypedRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
ruleName: "strict-boolean-expressions",
description: "Warns for an awaited value that is not a Promise.",
optionsDescription: "Not configurable.",
options: null,
optionExamples: ["true"],
type: "functionality",
typescriptOnly: true,
requiresTypeInfo: true,
};
/* tslint:enable:object-literal-sort-keys */

public static FAILURE_STRING = "'await' of non-Promise.";

public applyWithProgram(srcFile: ts.SourceFile, langSvc: ts.LanguageService): Lint.RuleFailure[] {
return this.applyWithWalker(new Walker(srcFile, this.getOptions(), langSvc.getProgram()));
}
}

class Walker extends Lint.ProgramAwareRuleWalker {
public visitNode(node: ts.Node) {
if (node.kind === ts.SyntaxKind.AwaitExpression &&
!couldBePromise(this.getTypeChecker().getTypeAtLocation((node as ts.AwaitExpression).expression))) {
this.addFailureAtNode(node, Rule.FAILURE_STRING);
}

super.visitNode(node);
}
}

function couldBePromise(type: ts.Type): boolean {
if (Lint.isTypeFlagSet(type, ts.TypeFlags.Any) || isPromiseType(type)) {
return true;
}

if (isUnionType(type)) {
return type.types.some(isPromiseType);
}

const bases = type.getBaseTypes();
return bases !== undefined && bases.some(couldBePromise);
}

function isPromiseType(type: ts.Type): boolean {
const { target } = type as ts.TypeReference;
const symbol = target && target.symbol;
return !!symbol && symbol.name === "Promise";
}

function isUnionType(type: ts.Type): type is ts.UnionType {
return Lint.isTypeFlagSet(type, ts.TypeFlags.Union);
}
26 changes: 26 additions & 0 deletions test/rules/await-promise/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
declare const num: Promise<number>;
declare const isAny: any;

async function f() {
await isAny;
await num;
await 0;
~~~~~~~ [0]
await (Math.random() > 0.5 ? num : 0);
await (Math.random() > 0.5 ? "" : 0);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [0]

class MyPromise extends Promise<number> {}
const myPromise: MyPromise = MyPromise.resolve(2);
await myPromise;

class Foo extends MyPromise {}
const foo: Foo = Foo.resolve(2);
await foo;

class Bar extends Array {}
await new Bar();
~~~~~~~~~~~~~~~ [0]
}

[0]: 'await' of non-Promise.
5 changes: 5 additions & 0 deletions test/rules/await-promise/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"target": "es6"
}
}
8 changes: 8 additions & 0 deletions test/rules/await-promise/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"linterOptions": {
"typeCheck": true
},
"rules": {
"await-promise": true
}
}

0 comments on commit 05e6041

Please sign in to comment.