Skip to content

Commit c31ad6f

Browse files
committed
Add tslint rules for microsoft#3994
1 parent ab9cf95 commit c31ad6f

5 files changed

Lines changed: 133 additions & 5 deletions

File tree

Jakefile.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,17 +770,34 @@ task("update-sublime", ["local", serverFile], function() {
770770
jake.cpR(serverFile + ".map", "../TypeScript-Sublime-Plugin/tsserver/");
771771
});
772772

773+
var tslintRuleDir = "scripts/tslint";
774+
var tslintRules = ([
775+
"nextLineRule",
776+
"noInferrableTypesRule"
777+
]);
778+
var tslintRulesFiles = tslintRules.map(function(p) {
779+
return path.join(tslintRuleDir, p + ".ts");
780+
});
781+
var tslintRulesOutFiles = tslintRules.map(function(p) {
782+
return path.join(builtLocalDirectory, "tslint", p + ".js");
783+
});
784+
desc("Compiles tslint rules to js");
785+
task("build-rules", tslintRulesOutFiles);
786+
tslintRulesFiles.forEach(function(ruleFile, i) {
787+
compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ true, /*noOutFile*/ true, /*generateDeclarations*/ false, path.join(builtLocalDirectory, "tslint"));
788+
});
789+
773790
// if the codebase were free of linter errors we could make jake runtests
774791
// run this task automatically
775792
desc("Runs tslint on the compiler sources");
776-
task("lint", [], function() {
793+
task("lint", ["build-rules"], function() {
777794
function success(f) { return function() { console.log('SUCCESS: No linter errors in ' + f + '\n'); }};
778795
function failure(f) { return function() { console.log('FAILURE: Please fix linting errors in ' + f + '\n') }};
779796

780797
var lintTargets = compilerSources.concat(harnessCoreSources);
781798
for (var i in lintTargets) {
782799
var f = lintTargets[i];
783-
var cmd = 'tslint -c tslint.json ' + f;
800+
var cmd = 'tslint --rules-dir built/local/tslint -c tslint.json ' + f;
784801
exec(cmd, success(f), failure(f));
785802
}
786803
}, { async: true });

scripts/tslint/nextLineRule.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/// <reference path="../../lib/typescriptServices.d.ts" />
2+
/// <reference path="../../node_modules/tslint/lib/tslint.d.ts" />
3+
4+
const OPTION_CATCH = "check-catch";
5+
const OPTION_ELSE = "check-else";
6+
7+
export class Rule extends Lint.Rules.AbstractRule {
8+
public static CATCH_FAILURE_STRING = "'catch' should be on the line following the previous block's ending curly brace";
9+
public static ELSE_FAILURE_STRING = "'else' should be on the line following the previous block's ending curly brace";
10+
11+
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
12+
return this.applyWithWalker(new NextLineWalker(sourceFile, this.getOptions()));
13+
}
14+
}
15+
16+
class NextLineWalker extends Lint.RuleWalker {
17+
public visitIfStatement(node: ts.IfStatement) {
18+
const sourceFile = node.getSourceFile();
19+
const thenStatement = node.thenStatement;
20+
21+
const elseStatement = node.elseStatement;
22+
if (!!elseStatement) {
23+
// find the else keyword
24+
const elseKeyword = getFirstChildOfKind(node, ts.SyntaxKind.ElseKeyword);
25+
if (this.hasOption(OPTION_ELSE) && !!elseKeyword) {
26+
const thenStatementEndLoc = sourceFile.getLineAndCharacterOfPosition(thenStatement.getEnd());
27+
const elseKeywordLoc = sourceFile.getLineAndCharacterOfPosition(elseKeyword.getStart());
28+
if (thenStatementEndLoc.line !== (elseKeywordLoc.line - 1)) {
29+
const failure = this.createFailure(elseKeyword.getStart(), elseKeyword.getWidth(), Rule.ELSE_FAILURE_STRING);
30+
this.addFailure(failure);
31+
}
32+
}
33+
}
34+
35+
super.visitIfStatement(node);
36+
}
37+
38+
public visitTryStatement(node: ts.TryStatement) {
39+
const sourceFile = node.getSourceFile();
40+
const catchClause = node.catchClause;
41+
42+
// "visit" try block
43+
const tryKeyword = node.getChildAt(0);
44+
const tryBlock = node.tryBlock;
45+
const tryOpeningBrace = tryBlock.getChildAt(0);
46+
47+
if (this.hasOption(OPTION_CATCH) && !!catchClause) {
48+
const tryClosingBrace = node.tryBlock.getChildAt(node.tryBlock.getChildCount() - 1);
49+
const catchKeyword = catchClause.getChildAt(0);
50+
const tryClosingBraceLoc = sourceFile.getLineAndCharacterOfPosition(tryClosingBrace.getEnd());
51+
const catchKeywordLoc = sourceFile.getLineAndCharacterOfPosition(catchKeyword.getStart());
52+
if (tryClosingBraceLoc.line !== (catchKeywordLoc.line - 1)) {
53+
const failure = this.createFailure(catchKeyword.getStart(), catchKeyword.getWidth(), Rule.CATCH_FAILURE_STRING);
54+
this.addFailure(failure);
55+
}
56+
}
57+
super.visitTryStatement(node);
58+
}
59+
}
60+
61+
function getFirstChildOfKind(node: ts.Node, kind: ts.SyntaxKind) {
62+
return node.getChildren().filter((child) => child.kind === kind)[0];
63+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/// <reference path="../../lib/typescriptServices.d.ts" />
2+
/// <reference path="../../node_modules/tslint/lib/tslint.d.ts" />
3+
4+
5+
export class Rule extends Lint.Rules.AbstractRule {
6+
public static FAILURE_STRING = "LHS type inferred by RHS expression, remove type annotation";
7+
8+
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
9+
const program = ts.createProgram([sourceFile.fileName], Lint.createCompilerOptions());
10+
return this.applyWithWalker(new InferrableTypeWalker(sourceFile, this.getOptions(), program));
11+
}
12+
}
13+
14+
class InferrableTypeWalker extends Lint.RuleWalker {
15+
constructor(file: ts.SourceFile, opts: Lint.IOptions, private program: ts.Program) {
16+
super(program.getSourceFile(file.fileName), opts);
17+
}
18+
19+
visitVariableStatement(node: ts.VariableStatement) {
20+
node.declarationList.declarations.forEach(e => {
21+
if (
22+
(!!e.type) &&
23+
(!!e.initializer) &&
24+
(this.program.getTypeChecker().getTypeAtLocation(e.type) === this.program.getTypeChecker().getContextualType(e.initializer))
25+
) {
26+
this.addFailure(this.createFailure(e.type.getStart(), e.type.getWidth(), Rule.FAILURE_STRING));
27+
}
28+
});
29+
30+
super.visitVariableStatement(node);
31+
}
32+
}

scripts/tslint/tsconfig.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"compilerOptions": {
3+
"noImplicitAny": true,
4+
"module": "commonjs",
5+
"outDir": "../../built/local/tslint"
6+
}
7+
}

tslint.json

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"spaces"
99
],
1010
"one-line": [true,
11-
"check-open-brace"
11+
"check-open-brace",
12+
"check-whitespace"
1213
],
1314
"no-unreachable": true,
1415
"no-use-before-declare": true,
@@ -21,14 +22,22 @@
2122
"check-branch",
2223
"check-operator",
2324
"check-separator",
24-
"check-type"
25+
"check-type",
26+
"check-module"
2527
],
2628
"typedef-whitespace": [true, {
2729
"call-signature": "nospace",
2830
"index-signature": "nospace",
2931
"parameter": "nospace",
3032
"property-declaration": "nospace",
3133
"variable-declaration": "nospace"
32-
}]
34+
}],
35+
"next-line": [true,
36+
"check-catch",
37+
"check-else"
38+
],
39+
"no-internal-module": true,
40+
"no-trailing-whitespace": true,
41+
"no-inferrable-types": true
3342
}
3443
}

0 commit comments

Comments
 (0)