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

Support for ERROR & WARN per rule #1738

Merged
merged 52 commits into from Mar 1, 2017
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
b002f7c
Support config object in tslint.json
olore Nov 15, 2016
c597b28
Use arrayify to make tslint.json cleaner if only 1 option, remove con…
olore Nov 15, 2016
9afc794
Handle warnings in stylishFormatter, verboseFormatter & vsoFormatter
olore Nov 16, 2016
35add24
* Formatter#format now takes failures,warnings,fixes (was failures, f…
olore Nov 16, 2016
ba2b4e2
Fix formatter tests
olore Nov 16, 2016
47e610e
Set warnings to default to [] if none passed in
olore Nov 16, 2016
135f215
Merge branch 'master' into enhanced-config-options
olore Nov 16, 2016
7c5e844
cleanup
olore Nov 17, 2016
024570a
Merge branch 'master' into enhanced-config-options
olore Nov 17, 2016
2c74270
Merge branch 'master' into enhanced-config-options
olore Nov 17, 2016
0af2c4c
cleanup for public unveiling, prepping for critique
olore Nov 17, 2016
f666d7c
RuleFailure -> RuleViolation
olore Nov 18, 2016
8fd7469
WIP - broken tests
olore Nov 21, 2016
9ab5e56
Merge branch 'master' into extend-rule-failure-to-include-warnings
olore Nov 22, 2016
4e6c572
Merge branch 'master' into extend-rule-failure-to-include-warnings
olore Nov 22, 2016
5087498
Cleanup after comparison with master
olore Nov 22, 2016
cc31e19
Merge branch 'master' into enhanced-config-options
olore Nov 22, 2016
2868256
revert RuleViolation -> RuleFailure because it would break all custom…
olore Nov 23, 2016
556fd4b
Complete the reverting of 'violation' back to 'failure'
olore Nov 23, 2016
d427ae3
Reverting some changes after comparing to master
olore Nov 23, 2016
7700ba1
Merge branch 'master' into enhanced-config-options
olore Nov 24, 2016
2302949
Revert tslint.json as we're still using the 'latest' from npm to lint…
olore Nov 24, 2016
bb5d808
Merge branch 'master' into enhanced-config-options
olore Nov 30, 2016
ec26ccc
Merge branch 'master' into enhanced-config-options
olore Dec 5, 2016
86e535f
Merge branch 'master' into enhanced-config-options
olore Jan 3, 2017
2401d38
Merge branch 'master' into enhanced-config-options
olore Jan 5, 2017
258c41e
Merge branch 'master' into fix
olore Jan 17, 2017
2ce7074
post-merge cleanup
olore Jan 17, 2017
ac388b8
pmdFormatter - Change priority depending on level (3 = error, 4 = war…
olore Jan 17, 2017
dcc3916
msbuildFormatter - output 'error' and 'warning' based on RuleLevel
olore Jan 17, 2017
68d94a3
checkstyleFormatter - output 'error' and 'warning' based on RuleLevel
olore Jan 17, 2017
33959e2
proseFormatter - output 'error' and 'warning' based on RuleLevel
olore Jan 17, 2017
b7aabcb
Accept warn/WARN/warning/WARNING in config
olore Jan 17, 2017
c0ac041
Add breaking changes to CHANGELOG.md
olore Jan 18, 2017
4d97b14
Merge branch 'master' into enhanced-config-options
olore Jan 19, 2017
dec3e12
Refactor - move config/ tests to config-legacy/
olore Jan 20, 2017
bb86f73
Create config tests for new tslint.json format and split legacy tests…
olore Jan 20, 2017
05d324e
Add valid values for to changelog
olore Jan 20, 2017
0683ae0
Merge branch 'master' into enhanced-config-options-tests
olore Jan 30, 2017
6e9967b
Cleanup after merge
olore Jan 30, 2017
1b0aedc
Merge branch 'master' into enhanced-config-options
olore Jan 31, 2017
18fb432
RuleLevel -> RuleSeverity to align with ESLint
olore Feb 6, 2017
f73e930
Tests for setting severity with and without options
olore Feb 6, 2017
8e826ff
Merge branch 'master' into enhanced-config-options
olore Feb 6, 2017
1999a4e
Merge branch 'master' into enhanced-config-options
olore Feb 11, 2017
1d0a6e0
Update scripts for config-legacy/
olore Feb 11, 2017
d1016da
remove legacy tests and add boolean config inheirt test
Feb 28, 2017
82c7036
Merge branch 'master' of github.com:palantir/tslint into enhanced-con…
Feb 28, 2017
e7e99cc
fix up constructor args
Feb 28, 2017
7d38ffb
use string literal union instead of enum
Feb 28, 2017
96c1f6f
populate rule severity in failures without specifying as argument for…
Mar 1, 2017
c15eb34
only exit(2) if errors > 0. Otherwise, exit(0) even with warnings
Mar 1, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/_data/formatters.json
Expand Up @@ -35,7 +35,7 @@
{
"formatterName": "prose",
"description": "The default formatter which outputs simple human-readable messages.",
"sample": "myFile.ts[1, 14]: Missing semicolon",
"sample": "ERROR: myFile.ts[1, 14]: Missing semicolon",
"consumer": "human"
},
{
Expand All @@ -49,7 +49,7 @@
"formatterName": "verbose",
"description": "The human-readable formatter which includes the rule name in messages.",
"descriptionDetails": "The output is the same as the prose formatter with the rule name included",
"sample": "(semicolon) myFile.ts[1, 14]: Missing semicolon",
"sample": "ERROR: (semicolon) myFile.ts[1, 14]: Missing semicolon",
"consumer": "human"
},
{
Expand Down
2 changes: 1 addition & 1 deletion docs/formatters/prose/index.html
@@ -1,7 +1,7 @@
---
formatterName: prose
description: The default formatter which outputs simple human-readable messages.
sample: 'myFile.ts[1, 14]: Missing semicolon'
sample: 'ERROR: myFile.ts[1, 14]: Missing semicolon'
consumer: human
layout: formatter
title: 'Formatter: prose'
Expand Down
2 changes: 1 addition & 1 deletion docs/formatters/verbose/index.html
Expand Up @@ -2,7 +2,7 @@
formatterName: verbose
description: The human-readable formatter which includes the rule name in messages.
descriptionDetails: The output is the same as the prose formatter with the rule name included
sample: '(semicolon) myFile.ts[1, 14]: Missing semicolon'
sample: 'ERROR: (semicolon) myFile.ts[1, 14]: Missing semicolon'
consumer: human
layout: formatter
title: 'Formatter: verbose'
Expand Down
17 changes: 11 additions & 6 deletions src/formatters/proseFormatter.ts
Expand Up @@ -17,21 +17,21 @@

import {AbstractFormatter} from "../language/formatter/abstractFormatter";
import {IFormatterMetadata} from "../language/formatter/formatter";
import {RuleFailure} from "../language/rule/rule";
import {RuleFailure, RuleLevel} from "../language/rule/rule";

export class Formatter extends AbstractFormatter {
/* tslint:disable:object-literal-sort-keys */
public static metadata: IFormatterMetadata = {
formatterName: "prose",
description: "The default formatter which outputs simple human-readable messages.",
sample: "myFile.ts[1, 14]: Missing semicolon",
sample: "ERROR: myFile.ts[1, 14]: Missing semicolon",
consumer: "human",
};
/* tslint:enable:object-literal-sort-keys */

public format(failures: RuleFailure[], fixes?: RuleFailure[]): string {
if (failures.length === 0 && (!fixes || fixes.length === 0)) {
return "";
return "\n";
}

let fixLines: string[] = [];
Expand All @@ -52,16 +52,21 @@ export class Formatter extends AbstractFormatter {
fixLines.push(""); // add a blank line between fixes and failures
}

let errorLines = failures.map((failure: RuleFailure) => {
return fixLines.concat(this.mapToMessages(failures))
.join("\n") + "\n";

}

private mapToMessages(failures: RuleFailure[]): string[] {
return failures.map((failure: RuleFailure) => {
const fileName = failure.getFileName();
const failureString = failure.getFailure();

const lineAndCharacter = failure.getStartPosition().getLineAndCharacter();
const positionTuple = `[${lineAndCharacter.line + 1}, ${lineAndCharacter.character + 1}]`;

return `${fileName}${positionTuple}: ${failureString}`;
return `${RuleLevel[failure.getRuleLevel()]}: ${fileName}${positionTuple}: ${failureString}`;
});

return fixLines.concat(errorLines).join("\n") + "\n";
}
}
35 changes: 22 additions & 13 deletions src/formatters/stylishFormatter.ts
Expand Up @@ -17,7 +17,7 @@

import {AbstractFormatter} from "../language/formatter/abstractFormatter";
import {IFormatterMetadata} from "../language/formatter/formatter";
import {RuleFailure} from "../language/rule/rule";
import {RuleFailure, RuleLevel} from "../language/rule/rule";

import * as colors from "colors";

Expand All @@ -39,10 +39,20 @@ export class Formatter extends AbstractFormatter {
/* tslint:enable:object-literal-sort-keys */

public format(failures: RuleFailure[]): string {
if (typeof failures[0] === "undefined") {
return "\n";
let outputLines = this.mapToMessages(failures);

// Removes initial blank line
if (outputLines[0] === "") {
outputLines.shift();
}

return outputLines.join("\n") + "\n";
}

private mapToMessages(failures: RuleFailure[]): string[] {
if (!failures) {
return [];
}
const outputLines: string[] = [];
const positionMaxSize = this.getPositionMaxSize(failures);
const ruleMaxSize = this.getRuleMaxSize(failures);
Expand Down Expand Up @@ -71,21 +81,20 @@ export class Formatter extends AbstractFormatter {
const lineAndCharacter = failure.getStartPosition().getLineAndCharacter();

let positionTuple = `${lineAndCharacter.line + 1}:${lineAndCharacter.character + 1}`;
positionTuple = this.pad(positionTuple, positionMaxSize);
positionTuple = colors.red(positionTuple);
positionTuple = this.pad(positionTuple, positionMaxSize);

// Ouput
if (failure.getRuleLevel() === RuleLevel.WARNING) {
positionTuple = colors.blue(RuleLevel[failure.getRuleLevel()] + ": " + positionTuple);
} else {
positionTuple = colors.red(RuleLevel[failure.getRuleLevel()] + ": " + positionTuple);
}

// Output
const output = `${positionTuple} ${ruleName} ${failureString}`;

outputLines.push(output);
}

// Removes initial blank line
if (outputLines[0] === "") {
outputLines.shift();
}

return outputLines.join("\n") + "\n\n";
return outputLines;
}

private pad(str: string, len: number): string {
Expand Down
15 changes: 10 additions & 5 deletions src/formatters/verboseFormatter.ts
Expand Up @@ -17,31 +17,36 @@

import {AbstractFormatter} from "../language/formatter/abstractFormatter";
import {IFormatterMetadata} from "../language/formatter/formatter";
import {RuleFailure} from "../language/rule/rule";
import {RuleFailure, RuleLevel} from "../language/rule/rule";

export class Formatter extends AbstractFormatter {
/* tslint:disable:object-literal-sort-keys */
public static metadata: IFormatterMetadata = {
formatterName: "verbose",
description: "The human-readable formatter which includes the rule name in messages.",
descriptionDetails: "The output is the same as the prose formatter with the rule name included",
sample: "(semicolon) myFile.ts[1, 14]: Missing semicolon",
sample: "ERROR: (semicolon) myFile.ts[1, 14]: Missing semicolon",
consumer: "human",
};
/* tslint:enable:object-literal-sort-keys */

public format(failures: RuleFailure[]): string {
const outputLines = failures.map((failure: RuleFailure) => {

return this.mapToMessages(failures)
.join("\n") + "\n";
}

private mapToMessages(failures: RuleFailure[]): string[] {
return failures.map((failure: RuleFailure) => {
const fileName = failure.getFileName();
const failureString = failure.getFailure();
const ruleName = failure.getRuleName();

const lineAndCharacter = failure.getStartPosition().getLineAndCharacter();
const positionTuple = "[" + (lineAndCharacter.line + 1) + ", " + (lineAndCharacter.character + 1) + "]";

return `(${ruleName}) ${fileName}${positionTuple}: ${failureString}`;
return `${RuleLevel[failure.getRuleLevel()]}: (${ruleName}) ${fileName}${positionTuple}: ${failureString}`;
});

return outputLines.join("\n") + "\n";
}
}
6 changes: 4 additions & 2 deletions src/formatters/vsoFormatter.ts
Expand Up @@ -33,8 +33,10 @@ export class Formatter extends AbstractFormatter {
};
/* tslint:enable:object-literal-sort-keys */

public format(failures: RuleFailure[]): string {
const outputLines = failures.map((failure: RuleFailure) => {
public format(failures: RuleFailure[], warnings: RuleFailure[] = []): string {
const all = failures.concat(warnings);

const outputLines = all.map((failure: RuleFailure) => {
const fileName = failure.getFileName();
const failureString = failure.getFailure();
const lineAndCharacter = failure.getStartPosition().getLineAndCharacter();
Expand Down
4 changes: 2 additions & 2 deletions src/language/formatter/formatter.ts
Expand Up @@ -49,8 +49,8 @@ export type ConsumerType = "human" | "machine";
export interface IFormatter {
/**
* Formats linter results
* @param {RuleFailure[]} failures Linter errors that were not fixed
* @param {RuleFailure[]} fixes Fixed linter errors. Available when the `--fix` argument is used on the command line
* @param {RuleFailure[]} failures Linter failures that were not fixed
* @param {RuleFailure[]} fixes Fixed linter failures. Available when the `--fix` argument is used on the command line
*/
format(failures: RuleFailure[], fixes?: RuleFailure[]): string;
}
17 changes: 15 additions & 2 deletions src/language/rule/abstractRule.ts
Expand Up @@ -17,22 +17,31 @@

import * as ts from "typescript";

import {arrayify} from "../../utils";
import {RuleWalker} from "../walker/ruleWalker";
import {IDisabledInterval, IOptions, IRule, IRuleMetadata, RuleFailure} from "./rule";
import {IDisabledInterval, IOptions, IRule, IRuleMetadata, RuleFailure, RuleLevel} from "./rule";

export abstract class AbstractRule implements IRule {
public static metadata: IRuleMetadata;
private options: IOptions;

constructor(ruleName: string, private value: any, disabledIntervals: IDisabledInterval[]) {
let ruleArguments: any[] = [];
let ruleLevel = RuleLevel.ERROR;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default rule level should be configurable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are you thinking here? Allow the config file to specify a default level for all? Sounds doable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, put a "defaultLevel" at the top level of tslint.json

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still working on this


if (Array.isArray(value) && value.length > 1) {
ruleArguments = value.slice(1);
} else if (value.options) {
ruleArguments = arrayify(value.options);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allow the tslint.json maintainer to no be forced to put a single value in an array

}

if (value.level === "warn") {
ruleLevel = RuleLevel.WARNING;
}

this.options = {
disabledIntervals,
ruleLevel,
ruleArguments,
ruleName,
};
Expand Down Expand Up @@ -60,6 +69,10 @@ export abstract class AbstractRule implements IRule {
return value[0];
}

return false;
if (value.level === "off" || value.level === "none") {
return false;
}

return true;
}
}
12 changes: 12 additions & 0 deletions src/language/rule/rule.ts
Expand Up @@ -81,8 +81,14 @@ export interface IRuleMetadata {

export type RuleType = "functionality" | "maintainability" | "style" | "typescript";

export enum RuleLevel {
"WARNING" = 1,
"ERROR",
}

export interface IOptions {
ruleArguments?: any[];
ruleLevel: RuleLevel;
ruleName: string;
disabledIntervals: IDisabledInterval[];
}
Expand Down Expand Up @@ -195,6 +201,7 @@ export class RuleFailure {
start: number,
end: number,
private failure: string,
private ruleLevel: RuleLevel,
private ruleName: string,
private fix?: Fix) {

Expand All @@ -207,6 +214,10 @@ export class RuleFailure {
return this.fileName;
}

public getRuleLevel() {
return this.ruleLevel;
}

public getRuleName() {
return this.ruleName;
}
Expand Down Expand Up @@ -237,6 +248,7 @@ export class RuleFailure {
failure: this.failure,
fix: this.fix,
name: this.fileName,
ruleLevel: RuleLevel[this.ruleLevel],
ruleName: this.ruleName,
startPosition: this.startPosition.toJson(),
};
Expand Down
6 changes: 4 additions & 2 deletions src/language/walker/ruleWalker.ts
Expand Up @@ -17,7 +17,7 @@

import * as ts from "typescript";

import {Fix, IDisabledInterval, IOptions, Replacement, RuleFailure} from "../rule/rule";
import {Fix, IDisabledInterval, IOptions, Replacement, RuleFailure, RuleLevel} from "../rule/rule";
import {doesIntersect} from "../utils";
import {SyntaxWalker} from "./syntaxWalker";

Expand All @@ -27,6 +27,7 @@ export class RuleWalker extends SyntaxWalker {
private options: any[];
private failures: RuleFailure[];
private disabledIntervals: IDisabledInterval[];
private ruleLevel: RuleLevel;
private ruleName: string;

constructor(private sourceFile: ts.SourceFile, options: IOptions) {
Expand All @@ -37,6 +38,7 @@ export class RuleWalker extends SyntaxWalker {
this.options = options.ruleArguments;
this.limit = this.sourceFile.getFullWidth();
this.disabledIntervals = options.disabledIntervals;
this.ruleLevel = options.ruleLevel;
this.ruleName = options.ruleName;
}

Expand Down Expand Up @@ -71,7 +73,7 @@ export class RuleWalker extends SyntaxWalker {
public createFailure(start: number, width: number, failure: string, fix?: Fix): RuleFailure {
const from = (start > this.limit) ? this.limit : start;
const to = ((start + width) > this.limit) ? this.limit : (start + width);
return new RuleFailure(this.sourceFile, from, to, failure, this.ruleName, fix);
return new RuleFailure(this.sourceFile, from, to, failure, this.ruleLevel, this.ruleName, fix);
}

public addFailure(failure: RuleFailure) {
Expand Down
3 changes: 2 additions & 1 deletion src/linter.ts
Expand Up @@ -31,7 +31,7 @@ import { EnableDisableRulesWalker } from "./enableDisableRules";
import { findFormatter } from "./formatterLoader";
import { ILinterOptions, LintResult } from "./index";
import { IFormatter } from "./language/formatter/formatter";
import { Fix, IRule, RuleFailure } from "./language/rule/rule";
import {Fix, IRule, RuleFailure, RuleLevel} from "./language/rule/rule";
import { TypedRule } from "./language/rule/typedRule";
import * as utils from "./language/utils";
import { loadRules } from "./ruleLoader";
Expand Down Expand Up @@ -177,6 +177,7 @@ class Linter {
// walk the code first to find all the intervals where rules are disabled
const rulesWalker = new EnableDisableRulesWalker(sourceFile, {
disabledIntervals: [],
ruleLevel: RuleLevel.ERROR,
ruleName: "",
});
rulesWalker.walk(sourceFile);
Expand Down
2 changes: 1 addition & 1 deletion src/rules/eoflineRule.ts
Expand Up @@ -45,7 +45,7 @@ export class Rule extends Lint.Rules.AbstractRule {
if (eofTokenFullText.length === 0 || eofTokenFullText.charAt(eofTokenFullText.length - 1) !== "\n") {
const start = eofToken.getStart();
return [
new Lint.RuleFailure(sourceFile, start, start, Rule.FAILURE_STRING, this.getOptions().ruleName),
new Lint.RuleFailure(sourceFile, start, start, Rule.FAILURE_STRING, this.getOptions().ruleLevel, this.getOptions().ruleName),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May be it worth to add access methods for ruleLevel and ruleName?

];
}

Expand Down
2 changes: 1 addition & 1 deletion src/rules/linebreakStyleRule.ts
Expand Up @@ -80,6 +80,6 @@ export class Rule extends Lint.Rules.AbstractRule {
// the line ending, which happens to be the start of the token.
const end = scanner.getStartPos();

return new Lint.RuleFailure(sourceFile, start, end, failure, this.getOptions().ruleName);
return new Lint.RuleFailure(sourceFile, start, end, failure, this.getOptions().ruleLevel, this.getOptions().ruleName);
}
}
2 changes: 1 addition & 1 deletion src/rules/maxFileLineCountRule.ts
Expand Up @@ -60,7 +60,7 @@ export class Rule extends Lint.Rules.AbstractRule {

if (lineCount > lineLimit && disabledIntervals.length === 0) {
const errorString = Rule.FAILURE_STRING_FACTORY(lineCount, lineLimit);
ruleFailures.push(new Lint.RuleFailure(sourceFile, 0, 1, errorString, this.getOptions().ruleName));
ruleFailures.push(new Lint.RuleFailure(sourceFile, 0, 1, errorString, this.getOptions().ruleLevel, this.getOptions().ruleName));
}

return ruleFailures;
Expand Down
2 changes: 1 addition & 1 deletion src/rules/maxLineLengthRule.ts
Expand Up @@ -70,7 +70,7 @@ export class Rule extends Lint.Rules.AbstractRule {
// that we are only over by the limit by exactly one and that the character we are over the
// limit by is a '\r' character which does not count against the limit
// (and thus we are not actually over the limit).
const ruleFailure = new Lint.RuleFailure(sourceFile, from, to - 1, errorString, this.getOptions().ruleName);
const ruleFailure = new Lint.RuleFailure(sourceFile, from, to - 1, errorString, this.getOptions().ruleLevel, this.getOptions().ruleName);
if (!Lint.doesIntersect(ruleFailure, disabledIntervals)) {
ruleFailures.push(ruleFailure);
}
Expand Down