/
checkMethodCallsRule.ts
92 lines (80 loc) · 3.28 KB
/
checkMethodCallsRule.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
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {bold} from 'chalk';
import {ProgramAwareRuleWalker, RuleFailure, Rules} from 'tslint';
import * as ts from 'typescript';
import {color} from '../material/color';
import {methodCallChecks} from '../material/component-data';
/**
* Rule that walks through every property access expression and updates properties that have
* been changed in favor of the new name.
*/
export class Rule extends Rules.TypedRule {
applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): RuleFailure[] {
return this.applyWithWalker(
new CheckMethodCallsWalker(sourceFile, this.getOptions(), program));
}
}
export class CheckMethodCallsWalker extends ProgramAwareRuleWalker {
visitNewExpression(expression: ts.NewExpression) {
const symbol = this.getTypeChecker().getTypeAtLocation(expression).symbol;
if (symbol) {
const className = symbol.name;
this.checkConstructor(expression, className);
}
}
visitCallExpression(expression: ts.CallExpression) {
if (expression.expression.kind !== ts.SyntaxKind.PropertyAccessExpression) {
const functionName = expression.getFirstToken().getText();
if (functionName === 'super') {
const superClassType = this.getTypeChecker().getTypeAtLocation(expression.expression);
const superClassName = superClassType.symbol && superClassType.symbol.name;
if (superClassType) {
this.checkConstructor(expression, superClassName);
}
}
return;
}
// TODO(mmalerba): This is probably a bad way to get the class node...
// Tokens are: [..., <host>, '.', <prop>], so back up 3.
const accessExp = expression.expression;
const classNode = accessExp.getChildAt(accessExp.getChildCount() - 3);
const methodNode = accessExp.getChildAt(accessExp.getChildCount() - 1);
const methodName = methodNode.getText();
const type = this.getTypeChecker().getTypeAtLocation(classNode);
const className = type.symbol && type.symbol.name;
const currentCheck = methodCallChecks
.find(data => data.method === methodName && data.className === className);
if (!currentCheck) {
return;
}
const failure = currentCheck.invalidArgCounts
.find(countData => countData.count === expression.arguments.length);
if (failure) {
this.addFailureAtNode(
expression,
`Found call to "${bold(className + '.' + methodName)}" with` +
` ${bold(String(failure.count))} arguments. ${color(failure.message)}`);
}
}
private checkConstructor(node: ts.NewExpression | ts.CallExpression, className: string) {
const currentCheck = methodCallChecks
.find(data => data.method === 'constructor' && data.className === className);
if (!currentCheck) {
return;
}
const failure = currentCheck.invalidArgCounts
.find(countData => !!node.arguments && countData.count === node.arguments.length);
if (failure) {
this.addFailureAtNode(
node,
`Found "${bold(className)}" constructed with ${bold(String(failure.count))} arguments.` +
` ${color(failure.message)}`);
}
}
}