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

[Breaking change] Remove createFix, and just use the Replacement(s) as the fix. (Minimal change) #2403

Merged
merged 1 commit into from
Mar 28, 2017
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
2 changes: 1 addition & 1 deletion docs/_data/formatters.json
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
{
"formatterName": "json",
"description": "Formats errors as simple JSON.",
"sample": "\n[\n {\n \"endPosition\": {\n \"character\": 13,\n \"line\": 0,\n \"position\": 13\n },\n \"failure\": \"Missing semicolon\",\n \"fix\": {\n \"innerRuleName\": \"semicolon\",\n \"innerReplacements\": [\n {\n \"innerStart\": 13,\n \"innerLength\": 0,\n \"innerText\": \";\"\n }\n ]\n },\n \"name\": \"myFile.ts\",\n \"ruleName\": \"semicolon\",\n \"startPosition\": {\n \"character\": 13,\n \"line\": 0,\n \"position\": 13\n }\n }\n]",
"sample": "\n[\n {\n \"endPosition\": {\n \"character\": 13,\n \"line\": 0,\n \"position\": 13\n },\n \"failure\": \"Missing semicolon\",\n \"fix\": {\n \"innerStart\": 13,\n \"innerLength\": 0,\n \"innerText\": \";\"\n },\n \"name\": \"myFile.ts\",\n \"ruleName\": \"semicolon\",\n \"startPosition\": {\n \"character\": 13,\n \"line\": 0,\n \"position\": 13\n }\n }\n]",
"consumer": "machine"
},
{
11 changes: 3 additions & 8 deletions docs/formatters/json/index.html
Original file line number Diff line number Diff line change
@@ -12,14 +12,9 @@
},
"failure": "Missing semicolon",
"fix": {
"innerRuleName": "semicolon",
"innerReplacements": [
{
"innerStart": 13,
"innerLength": 0,
"innerText": ";"
}
]
"innerStart": 13,
"innerLength": 0,
"innerText": ";"
},
"name": "myFile.ts",
"ruleName": "semicolon",
11 changes: 3 additions & 8 deletions src/formatters/jsonFormatter.ts
Original file line number Diff line number Diff line change
@@ -36,14 +36,9 @@ export class Formatter extends AbstractFormatter {
},
"failure": "Missing semicolon",
"fix": {
"innerRuleName": "semicolon",
"innerReplacements": [
{
"innerStart": 13,
"innerLength": 0,
"innerText": ";"
}
]
"innerStart": 13,
"innerLength": 0,
"innerText": ";"
},
"name": "myFile.ts",
"ruleName": "semicolon",
37 changes: 11 additions & 26 deletions src/language/rule/rule.ts
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@

import * as ts from "typescript";

import {arrayify, flatMap} from "../../utils";
import {IWalker} from "../walker";

export interface IRuleMetadata {
@@ -132,12 +133,20 @@ export function isTypedRule(rule: IRule): rule is ITypedRule {
}

export class Replacement {
public static applyFixes(content: string, fixes: Fix[]): string {
return this.applyAll(content, flatMap(fixes, arrayify));
}

public static applyAll(content: string, replacements: Replacement[]) {
// sort in reverse so that diffs are properly applied
replacements.sort((a, b) => b.end - a.end);
return replacements.reduce((text, r) => r.apply(text), content);
}

public static replaceNode(node: ts.Node, text: string, sourceFile?: ts.SourceFile): Replacement {
return this.replaceFromTo(node.getStart(sourceFile), node.getEnd(), text);
}

public static replaceFromTo(start: number, end: number, text: string) {
return new Replacement(start, end - start, text);
}
@@ -178,32 +187,6 @@ export class Replacement {
}
}

export class Fix {
public static applyAll(content: string, fixes: Fix[]) {
// accumulate replacements
let replacements: Replacement[] = [];
for (const fix of fixes) {
replacements = replacements.concat(fix.replacements);
}
return Replacement.applyAll(content, replacements);
}

constructor(private innerRuleName: string, private innerReplacements: Replacement[]) {
}

get ruleName() {
return this.innerRuleName;
}

get replacements() {
return this.innerReplacements;
}

public apply(content: string) {
return Replacement.applyAll(content, this.innerReplacements);
}
}

export class RuleFailurePosition {
constructor(private position: number, private lineAndCharacter: ts.LineAndCharacter) {
}
@@ -234,6 +217,8 @@ export class RuleFailurePosition {
}
}

export type Fix = Replacement | Replacement[];

export class RuleFailure {
private fileName: string;
private startPosition: RuleFailurePosition;
4 changes: 0 additions & 4 deletions src/language/walker/ruleWalker.ts
Original file line number Diff line number Diff line change
@@ -110,8 +110,4 @@ export class RuleWalker extends SyntaxWalker implements IWalker {
public getRuleName(): string {
return this.ruleName;
}

public createFix(...replacements: Replacement[]): Fix {
return new Fix(this.ruleName, replacements);
}
}
6 changes: 1 addition & 5 deletions src/language/walker/walkContext.ts
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@

import * as ts from "typescript";

import { Fix, Replacement, RuleFailure } from "../rule/rule";
import { Fix, RuleFailure } from "../rule/rule";

export class WalkContext<T> {
public readonly failures: RuleFailure[] = [];
@@ -40,8 +40,4 @@ export class WalkContext<T> {
public addFailureAtNode(node: ts.Node, failure: string, fix?: Fix) {
this.addFailure(node.getStart(this.sourceFile), node.getEnd(), failure, fix);
}

public createFix(...replacements: Replacement[]) {
return new Fix(this.ruleName, replacements);
}
}
4 changes: 2 additions & 2 deletions src/linter.ts
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ import { isError, showWarningOnce } from "./error";
import { findFormatter } from "./formatterLoader";
import { ILinterOptions, LintResult } from "./index";
import { IFormatter } from "./language/formatter/formatter";
import { Fix, IRule, isTypedRule, RuleFailure, RuleSeverity } from "./language/rule/rule";
import { Fix, IRule, isTypedRule, Replacement, RuleFailure, RuleSeverity } from "./language/rule/rule";
import * as utils from "./language/utils";
import { loadRules } from "./ruleLoader";
import { arrayify, dedent } from "./utils";
@@ -108,7 +108,7 @@ class Linter {
source = fs.readFileSync(fileName, { encoding: "utf-8" });
if (fixes.length > 0) {
this.fixes = this.fixes.concat(ruleFailures);
source = Fix.applyAll(source, fixes);
source = Replacement.applyFixes(source, fixes);
fs.writeFileSync(fileName, source, { encoding: "utf-8" });

// reload AST if file is modified
4 changes: 2 additions & 2 deletions src/rules/alignRule.ts
Original file line number Diff line number Diff line change
@@ -117,10 +117,10 @@ class AlignWalker extends Lint.AbstractWalker<Options> {
const diff = alignToColumn - pos.character;
let fix: Lint.Fix | undefined;
if (0 < diff) {
fix = this.createFix(Lint.Replacement.appendText(start, " ".repeat(diff)));
fix = Lint.Replacement.appendText(start, " ".repeat(diff));
} else if (node.pos <= start + diff && /^\s+$/.test(sourceFile.text.substring(start + diff, start))) {
// only delete text if there is only whitespace
fix = this.createFix(Lint.Replacement.deleteText(start + diff, -diff));
fix = Lint.Replacement.deleteText(start + diff, -diff);
}
this.addFailure(start, node.end, kind + Rule.FAILURE_STRING_SUFFIX, fix);
}
10 changes: 5 additions & 5 deletions src/rules/arrayTypeRule.ts
Original file line number Diff line number Diff line change
@@ -65,11 +65,11 @@ class ArrayTypeWalker extends Lint.RuleWalker {
// Add a space if the type is preceded by 'as' and the node has no leading whitespace
const space = !parens && node.parent!.kind === ts.SyntaxKind.AsExpression &&
node.getStart() === node.getFullStart() ? " " : "";
const fix = this.createFix(
const fix = [
this.createReplacement(typeName.getStart(), parens, space + "Array<"),
// Delete the square brackets and replace with an angle bracket
this.createReplacement(typeName.getEnd() - parens, node.getEnd() - typeName.getEnd() + parens, ">"),
);
];
this.addFailureAtNode(node, failureString, fix);
}

@@ -83,20 +83,20 @@ class ArrayTypeWalker extends Lint.RuleWalker {
const typeArgs = node.typeArguments;
if (!typeArgs || typeArgs.length === 0) {
// Create an 'any' array
const fix = this.createFix(this.createReplacement(node.getStart(), node.getWidth(), "any[]"));
const fix = this.createReplacement(node.getStart(), node.getWidth(), "any[]");
this.addFailureAtNode(node, failureString, fix);
} else if (typeArgs && typeArgs.length === 1 && (!this.hasOption(OPTION_ARRAY_SIMPLE) || this.isSimpleType(typeArgs[0]))) {
const type = typeArgs[0];
const typeStart = type.getStart();
const typeEnd = type.getEnd();
const parens = type.kind === ts.SyntaxKind.UnionType ||
type.kind === ts.SyntaxKind.FunctionType || type.kind === ts.SyntaxKind.IntersectionType;
const fix = this.createFix(
const fix = [
// Delete Array and the first angle bracket
this.createReplacement(node.getStart(), typeStart - node.getStart(), parens ? "(" : ""),
// Delete the last angle bracket and replace with square brackets
this.createReplacement(typeEnd, node.getEnd() - typeEnd, (parens ? ")" : "") + "[]"),
);
];
this.addFailureAtNode(node, failureString, fix);
}
}
8 changes: 4 additions & 4 deletions src/rules/arrowParensRule.ts
Original file line number Diff line number Diff line change
@@ -65,17 +65,17 @@ function walk(ctx: Lint.WalkContext<Options>) {
const parameter = node.parameters[0];
const start = parameter.getStart(ctx.sourceFile);
const end = parameter.end;
ctx.addFailure(start, end, Rule.FAILURE_STRING_MISSING, ctx.createFix(
ctx.addFailure(start, end, Rule.FAILURE_STRING_MISSING, [
Lint.Replacement.appendText(start, "("),
Lint.Replacement.appendText(end, ")"),
));
]);
}
} else if (ctx.options.banSingleArgParens) {
const closeParen = getChildOfKind(node, ts.SyntaxKind.CloseParenToken)!;
ctx.addFailureAtNode(node.parameters[0], Rule.FAILURE_STRING_EXISTS, ctx.createFix(
ctx.addFailureAtNode(node.parameters[0], Rule.FAILURE_STRING_EXISTS, [
Lint.Replacement.deleteText(openParen.end - 1, 1),
Lint.Replacement.deleteText(closeParen.end - 1, 1),
));
]);
}
}
return ts.forEachChild(node, cb);
4 changes: 2 additions & 2 deletions src/rules/arrowReturnShorthandRule.ts
Original file line number Diff line number Diff line change
@@ -78,7 +78,7 @@ class Walker extends Lint.RuleWalker {

const anyComments = hasComments(arrow) || hasComments(openBrace) || hasComments(statement) || hasComments(returnKeyword) ||
hasComments(expr) || (semicolon && hasComments(semicolon)) || hasComments(closeBrace);
return anyComments ? undefined : this.createFix(
return anyComments ? undefined : [
// Object literal must be wrapped in `()`
...(expr.kind === ts.SyntaxKind.ObjectLiteralExpression ? [
this.appendText(expr.getStart(), "("),
@@ -90,7 +90,7 @@ class Walker extends Lint.RuleWalker {
this.deleteFromTo(statement.getStart(), expr.getStart()),
// " }" (may include semicolon)
this.deleteFromTo(expr.end, closeBrace.end),
);
];

function hasComments(node: ts.Node): boolean {
return ts.getTrailingCommentRanges(text, node.getEnd()) !== undefined;
4 changes: 1 addition & 3 deletions src/rules/eoflineRule.ts
Original file line number Diff line number Diff line change
@@ -45,9 +45,7 @@ export class Rule extends Lint.Rules.AbstractRule {
let fix: Lint.Fix | undefined;
const lines = sourceFile.getLineStarts();
if (lines.length > 1) {
fix = new Lint.Fix(this.ruleName, [
Lint.Replacement.appendText(length, sourceFile.text[lines[1] - 2] === "\r" ? "\r\n" : "\n"),
]);
fix = Lint.Replacement.appendText(length, sourceFile.text[lines[1] - 2] === "\r" ? "\r\n" : "\n");
}

return this.filterFailures([
8 changes: 2 additions & 6 deletions src/rules/linebreakStyleRule.ts
Original file line number Diff line number Diff line change
@@ -59,14 +59,10 @@ function walk(ctx: Lint.WalkContext<boolean>) {
const lineEnd = lineStarts[i] - 1;
if (sourceText[lineEnd - 1] === "\r") {
if (!expectedCr) {
ctx.addFailure(lineStarts[i - 1], lineEnd - 1, Rule.FAILURE_LF, ctx.createFix(
Lint.Replacement.deleteText(lineEnd - 1, 1),
));
ctx.addFailure(lineStarts[i - 1], lineEnd - 1, Rule.FAILURE_LF, Lint.Replacement.deleteText(lineEnd - 1, 1));
}
} else if (expectedCr) {
ctx.addFailure(lineStarts[i - 1], lineEnd, Rule.FAILURE_CRLF, ctx.createFix(
Lint.Replacement.appendText(lineEnd, "\r"),
));
ctx.addFailure(lineStarts[i - 1], lineEnd, Rule.FAILURE_CRLF, Lint.Replacement.appendText(lineEnd, "\r"));
}
}
}
20 changes: 1 addition & 19 deletions src/rules/memberOrderingRule.ts
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@
import * as ts from "typescript";

import * as Lint from "../index";
import {flatMap, mapDefined} from "../utils";

const OPTION_ORDER = "order";
const OPTION_ALPHABETIZE = "alphabetize";
@@ -478,22 +479,3 @@ function nameString(name: ts.PropertyName): string {
return "";
}
}

function mapDefined<T, U>(inputs: T[], getOutput: (input: T) => U | undefined): U[] {
const out = [];
for (const input of inputs) {
const output = getOutput(input);
if (output !== undefined) {
out.push(output);
}
}
return out;
}

function flatMap<T, U>(inputs: T[], getOutputs: (input: T) => U[]): U[] {
const out = [];
for (const input of inputs) {
out.push(...getOutputs(input));
}
return out;
}
4 changes: 2 additions & 2 deletions src/rules/noAngleBracketTypeAssertionRule.ts
Original file line number Diff line number Diff line change
@@ -49,10 +49,10 @@ function walk(ctx: Lint.WalkContext<void>) {
return ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
if (isTypeAssertion(node)) {
const start = node.getStart(ctx.sourceFile);
ctx.addFailure(start, node.end, Rule.FAILURE_STRING, ctx.createFix(
ctx.addFailure(start, node.end, Rule.FAILURE_STRING, [
Lint.Replacement.appendText(node.end, ` as ${ node.type.getText(ctx.sourceFile) }`),
Lint.Replacement.deleteFromTo(start, node.expression.getStart(ctx.sourceFile)),
));
]);
}
return ts.forEachChild(node, cb);
});
4 changes: 1 addition & 3 deletions src/rules/noAnyRule.ts
Original file line number Diff line number Diff line change
@@ -47,9 +47,7 @@ function walk(ctx: Lint.WalkContext<void>) {
return ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
if (node.kind === ts.SyntaxKind.AnyKeyword) {
const start = node.end - 3;
return ctx.addFailure(start, node.end, Rule.FAILURE_STRING, ctx.createFix(
new Lint.Replacement(start, 3, "{}"),
));
return ctx.addFailure(start, node.end, Rule.FAILURE_STRING, new Lint.Replacement(start, 3, "{}"));
}
return ts.forEachChild(node, cb);
});
2 changes: 1 addition & 1 deletion src/rules/noBooleanLiteralCompareRule.ts
Original file line number Diff line number Diff line change
@@ -74,7 +74,7 @@ class Walker extends Lint.ProgramAwareRuleWalker {
}
}

this.addFailureAtNode(expression, Rule.FAILURE_STRING(negate), this.createFix(...replacements));
this.addFailureAtNode(expression, Rule.FAILURE_STRING(negate), replacements);
}
}

4 changes: 2 additions & 2 deletions src/rules/noConsecutiveBlankLinesRule.ts
Original file line number Diff line number Diff line change
@@ -90,14 +90,14 @@ function walk(ctx: Lint.WalkContext<number>) {
const templateRanges = getTemplateRanges(ctx.sourceFile);
for (const possibleFailure of possibleFailures) {
if (!templateRanges.some((template) => template.pos < possibleFailure.pos && possibleFailure.pos < template.end)) {
ctx.addFailureAt(possibleFailure.pos, 1, failureString, ctx.createFix(
ctx.addFailureAt(possibleFailure.pos, 1, failureString, [
Lint.Replacement.deleteFromTo(
// special handling for fixing blank lines at the end of the file
// to fix this we need to cut off the line break of the last allowed blank line, too
possibleFailure.end === sourceText.length ? getStartOfLineBreak(sourceText, possibleFailure.pos) : possibleFailure.pos,
possibleFailure.end,
),
));
]);
}
}
}
2 changes: 1 addition & 1 deletion src/rules/noInferrableTypesRule.ts
Original file line number Diff line number Diff line change
@@ -131,7 +131,7 @@ class NoInferrableTypesWalker extends Lint.AbstractWalker<IOptions> {
if (failure != null) {
this.addFailureAtNode(node.type,
Rule.FAILURE_STRING_FACTORY(failure),
this.createFix(Lint.Replacement.deleteFromTo(node.name.end, node.type.end)),
Lint.Replacement.deleteFromTo(node.name.end, node.type.end),
);
}
}
7 changes: 4 additions & 3 deletions src/rules/noStringThrowRule.ts
Original file line number Diff line number Diff line change
@@ -45,9 +45,10 @@ class Walker extends Lint.RuleWalker {
public visitThrowStatement(node: ts.ThrowStatement) {
const {expression} = node;
if (this.stringConcatRecursive(expression)) {
const fix = this.createFix(this.createReplacement(expression.getStart(),
expression.getEnd() - expression.getStart(),
`new Error(${expression.getText()})`));
const fix = this.createReplacement(
expression.getStart(),
expression.getEnd() - expression.getStart(),
`new Error(${expression.getText()})`);
this.addFailure(this.createFailure(
node.getStart(), node.getWidth(), Rule.FAILURE_STRING, fix));
}
4 changes: 2 additions & 2 deletions src/rules/noTrailingWhitespaceRule.ts
Original file line number Diff line number Diff line change
@@ -100,9 +100,9 @@ function walk(ctx: Lint.WalkContext<Options>) {
}
for (const possibleFailure of possibleFailures) {
if (!excludedRanges.some((range) => range.pos < possibleFailure.pos && possibleFailure.pos < range.end)) {
ctx.addFailure(possibleFailure.pos, possibleFailure.end, Rule.FAILURE_STRING, ctx.createFix(
ctx.addFailure(possibleFailure.pos, possibleFailure.end, Rule.FAILURE_STRING,
Lint.Replacement.deleteFromTo(possibleFailure.pos, possibleFailure.end),
));
);
}
}
}
Loading