Skip to content
This repository has been archived by the owner on Jul 15, 2023. It is now read-only.

Converted no-function-expression to use a walk function #802

Merged
merged 1 commit into from
Jan 28, 2019
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: 42 additions & 44 deletions src/noFunctionExpressionRule.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as ts from 'typescript';
import * as Lint from 'tslint';
import * as tsutils from 'tsutils';

import { AstUtils } from './utils/AstUtils';
import { ExtendedMetadata } from './utils/ExtendedMetadata';
Expand All @@ -23,63 +24,60 @@ export class Rule extends Lint.Rules.AbstractRule {
public static FAILURE_STRING: string = 'Use arrow function instead of function expression';

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new NoFunctionExpressionRuleWalker(sourceFile, this.getOptions()));
return this.applyWithFunction(sourceFile, walk);
}
}

class NoFunctionExpressionRuleWalker extends Lint.RuleWalker {
private readonly allowGenericFunctionExpression: boolean = false;
function walk(ctx: Lint.WalkContext<void>) {
const allowGenericFunctionExpression = AstUtils.getLanguageVariant(ctx.sourceFile) === ts.LanguageVariant.JSX;

constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) {
super(sourceFile, options);
function cb(node: ts.Node): void {
if (tsutils.isFunctionExpression(node)) {
const walker = new SingleFunctionWalker();
node.getChildren().forEach((child: ts.Node) => {
walker.walk(child);
});

if (AstUtils.getLanguageVariant(sourceFile) === ts.LanguageVariant.JSX) {
this.allowGenericFunctionExpression = true;
}
}

protected visitFunctionExpression(node: ts.FunctionExpression): void {
const walker = new SingleFunctionWalker(this.getSourceFile(), this.getOptions());
node.getChildren().forEach((child: ts.Node) => {
walker.walk(child);
});

const isGenericFunctionInTSX = this.allowGenericFunctionExpression && walker.isGenericFunction;
// function expression that access 'this' is allowed
if (
!walker.isAccessingThis &&
!node.asteriskToken &&
// generic function expression in .tsx file is allowed
!isGenericFunctionInTSX
) {
this.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING);
const isGenericFunctionInTSX = allowGenericFunctionExpression && walker.isGenericFunction;
// function expression that access 'this' is allowed
if (
!walker.isAccessingThis &&
!node.asteriskToken &&
// generic function expression in .tsx file is allowed
!isGenericFunctionInTSX
) {
ctx.addFailureAt(node.getStart(), node.getWidth(), Rule.FAILURE_STRING);
}
}

super.visitFunctionExpression(node);
return ts.forEachChild(node, cb);
}

return ts.forEachChild(ctx.sourceFile, cb);
}

class SingleFunctionWalker extends Lint.RuleWalker {
class SingleFunctionWalker {
public isAccessingThis: boolean = false;
public isGenericFunction: boolean = false;
protected visitNode(node: ts.Node): void {
if (node.getText() === 'this') {
this.isAccessingThis = true;
}
super.visitNode(node);
}

protected visitTypeReference(node: ts.TypeReferenceNode): void {
this.isGenericFunction = true;
super.visitTypeReference(node);
}
public walk(root: ts.Node) {
const cb = (node: ts.Node) => {
if (node.getText() === 'this') {
this.isAccessingThis = true;
}

/* tslint:disable:no-empty */
protected visitFunctionExpression(): void {
// do not visit inner blocks
}
protected visitArrowFunction(): void {
// do not visit inner blocks
if (tsutils.isFunctionExpression(node) || tsutils.isArrowFunction(node)) {
// do not visit inner blocks
return;
}

if (tsutils.isTypeReferenceNode(node)) {
this.isGenericFunction = true;
}

ts.forEachChild(node, cb);
};

cb(root);
}
/* tslint:enable:no-empty */
}