UNPKG

2.9 kBPlain TextView Raw
1import * as ts from 'typescript';
2import * as Lint from 'tslint';
3
4import { ErrorTolerantWalker } from './utils/ErrorTolerantWalker';
5import { ExtendedMetadata } from './utils/ExtendedMetadata';
6
7/**
8 * Implementation of the id-length rule.
9 */
10export class Rule extends Lint.Rules.AbstractRule {
11 public static metadata: ExtendedMetadata = {
12 ruleName: 'no-for-each-push',
13 type: 'maintainability',
14 description: 'Enforce using Array.prototype.map instead of Array.prototype.forEach and Array.prototype.push.',
15 options: null,
16 optionsDescription: '',
17 typescriptOnly: true,
18 issueClass: 'Non-SDL',
19 issueType: 'Warning',
20 severity: 'Important',
21 level: 'Opportunity for Excellence',
22 group: 'Correctness',
23 recommendation: 'true,',
24 commonWeaknessEnumeration: '',
25 };
26
27 public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
28 return this.applyWithWalker(new NoForeachPushRuleWalker(sourceFile, this.getOptions()));
29 }
30
31 public static FAILURE_STRING: string =
32 'Do not use Array.prototype.push inside of Array.prototype.forEach. ' + 'Use Array.prototype.map instead to replace both.';
33}
34
35class NoForeachPushRuleWalker extends ErrorTolerantWalker {
36 protected visitPropertyAccessExpression(node: ts.PropertyAccessExpression): void {
37 this.checkAndReport(node);
38 super.visitPropertyAccessExpression(node);
39 }
40
41 private checkAndReport(node: ts.PropertyAccessExpression): void {
42 const isCallExpression = ts.isCallExpression(node.parent);
43 const isForEach = node.name.text === 'forEach';
44 // console.log('checkAndReport', isCallExpression, isForEach); // tslint:disable-line
45 if (isCallExpression && isForEach) {
46 if (this.doesCallPush(node)) {
47 this.addFailureAtNode(node.parent, Rule.FAILURE_STRING);
48 }
49 }
50 }
51
52 private doesCallPush(node: ts.PropertyAccessExpression) {
53 const walker = new PushCallWalker();
54 walker.walk(node.parent);
55 return walker.isFound;
56 }
57}
58
59class PushCallWalker extends Lint.SyntaxWalker {
60 private foundPush: boolean = false;
61 private foundIf: boolean = false;
62
63 protected visitPropertyAccessExpression(node: ts.PropertyAccessExpression): void {
64 const isCallExpression = ts.isCallExpression(node.parent);
65 const isPush = node.name.text === 'push';
66 // console.log('PushCallWalker', isCallExpression, isPush, node); // tslint:disable-line
67 if (isCallExpression && isPush) {
68 this.foundPush = true;
69 return;
70 }
71 super.visitPropertyAccessExpression(node);
72 }
73
74 protected visitIfStatement(): void {
75 this.foundIf = true;
76 return;
77 }
78
79 public get isFound(): boolean {
80 return !this.foundIf && this.foundPush;
81 }
82}