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

Commit

Permalink
convert no-unnecessary-field-initialization rule to use a walk functi…
Browse files Browse the repository at this point in the history
…on (#804)
  • Loading branch information
drexler authored and Josh Goldberg committed Feb 2, 2019
1 parent e95fc3e commit 970a21e
Showing 1 changed file with 52 additions and 47 deletions.
99 changes: 52 additions & 47 deletions src/noUnnecessaryFieldInitializationRule.ts
@@ -1,5 +1,6 @@
import * as ts from 'typescript';
import * as Lint from 'tslint';
import * as tsutils from 'tsutils';

import { ExtendedMetadata } from './utils/ExtendedMetadata';
import { AstUtils } from './utils/AstUtils';
Expand All @@ -24,78 +25,45 @@ export class Rule extends Lint.Rules.AbstractRule {
};

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

class UnnecessaryFieldInitializationRuleWalker extends Lint.RuleWalker {
private fieldInitializations: { [index: string]: string | undefined } = {};
function walk(ctx: Lint.WalkContext<void>) {
let fieldInitializations: { [index: string]: string | undefined } = {};

protected visitClassDeclaration(node: ts.ClassDeclaration): void {
this.fieldInitializations = {};
node.members.forEach(
(member: ts.ClassElement): void => {
if (member.kind === ts.SyntaxKind.PropertyDeclaration) {
this.visitPropertyDeclaration(<ts.PropertyDeclaration>member);
} else if (member.kind === ts.SyntaxKind.Constructor) {
this.visitConstructorDeclaration(<ts.ConstructorDeclaration>member);
}
}
);
this.fieldInitializations = {};
// do not call super.visitClass as a performance enhancement
}

protected visitPropertyDeclaration(node: ts.PropertyDeclaration): void {
const initializer = node.initializer;
if (node.name.kind === ts.SyntaxKind.Identifier) {
const fieldName: string = 'this.' + (<ts.Identifier>node.name).getText();
if (initializer === undefined) {
this.fieldInitializations[fieldName] = undefined;
} else if (AstUtils.isConstant(initializer)) {
this.fieldInitializations[fieldName] = initializer.getText();
}
}
if (initializer !== undefined && AstUtils.isUndefined(initializer)) {
// you should never initialize a field to undefined.
const start: number = initializer.getStart();
const width: number = initializer.getWidth();
this.addFailureAt(start, width, FAILURE_UNDEFINED_INIT + node.name.getText());
}
}

protected visitConstructorDeclaration(node: ts.ConstructorDeclaration): void {
function visitConstructorDeclaration(node: ts.ConstructorDeclaration): void {
if (node.body !== undefined) {
node.body.statements.forEach(
(statement: ts.Statement): void => {
if (statement.kind === ts.SyntaxKind.ExpressionStatement) {
const expression: ts.Expression = (<ts.ExpressionStatement>statement).expression;
if (expression.kind === ts.SyntaxKind.BinaryExpression) {
const binaryExpression: ts.BinaryExpression = <ts.BinaryExpression>expression;
if (tsutils.isExpressionStatement(statement)) {
const expression: ts.Expression = statement.expression;
if (tsutils.isBinaryExpression(expression)) {
const binaryExpression: ts.BinaryExpression = expression;

const property: ts.Expression = binaryExpression.left;
const propertyName: string = property.getText();
// check to see if a field is being assigned in the constructor
if (Object.keys(this.fieldInitializations).indexOf(propertyName) > -1) {
if (Object.keys(fieldInitializations).indexOf(propertyName) > -1) {
if (AstUtils.isUndefined(binaryExpression.right)) {
// field is being assigned to undefined... create error if the field already has that value
if (Object.keys(this.fieldInitializations).indexOf(propertyName) > -1) {
if (Object.keys(fieldInitializations).indexOf(propertyName) > -1) {
// make sure the field was declared as undefined
const fieldInitValue = this.fieldInitializations[propertyName];
const fieldInitValue = fieldInitializations[propertyName];
if (fieldInitValue === undefined) {
const start: number = property.getStart();
const width: number = property.getWidth();
this.addFailureAt(start, width, FAILURE_UNDEFINED_INIT + property.getText());
ctx.addFailureAt(start, width, FAILURE_UNDEFINED_INIT + property.getText());
}
}
} else if (AstUtils.isConstant(binaryExpression.right)) {
// field is being assigned a constant... create error if the field already has that value
const fieldInitValue = this.fieldInitializations[propertyName];
const fieldInitValue = fieldInitializations[propertyName];
if (fieldInitValue === binaryExpression.right.getText()) {
const start: number = binaryExpression.getStart();
const width: number = binaryExpression.getWidth();
const message: string = FAILURE_UNDEFINED_DUPE + binaryExpression.getText();
this.addFailureAt(start, width, message);
ctx.addFailureAt(start, width, message);
}
}
}
Expand All @@ -105,4 +73,41 @@ class UnnecessaryFieldInitializationRuleWalker extends Lint.RuleWalker {
);
}
}

function visitPropertyDeclaration(node: ts.PropertyDeclaration): void {
const initializer = node.initializer;
if (tsutils.isIdentifier(node.name)) {
const fieldName: string = 'this.' + node.name.getText();
if (initializer === undefined) {
fieldInitializations[fieldName] = undefined;
} else if (AstUtils.isConstant(initializer)) {
fieldInitializations[fieldName] = initializer.getText();
}
}
if (initializer !== undefined && AstUtils.isUndefined(initializer)) {
// you should never initialize a field to undefined.
const start: number = initializer.getStart();
const width: number = initializer.getWidth();
ctx.addFailureAt(start, width, FAILURE_UNDEFINED_INIT + node.name.getText());
}
}

function cb(node: ts.Node): void {
if (tsutils.isClassDeclaration(node)) {
fieldInitializations = {};
node.members.forEach(
(member: ts.ClassElement): void => {
if (tsutils.isPropertyDeclaration(member)) {
visitPropertyDeclaration(member);
} else if (tsutils.isConstructorDeclaration(member)) {
visitConstructorDeclaration(member);
}
}
);

fieldInitializations = {};
}
}

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

0 comments on commit 970a21e

Please sign in to comment.