Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tslint rules for #3994 #4458

Merged
merged 6 commits into from
Sep 18, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Add tslint rules for #3994
  • Loading branch information
weswigham committed Aug 26, 2015
commit c31ad6fb28e9daed09d585558edfc2f39ad4a3a7
21 changes: 19 additions & 2 deletions Jakefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -770,17 +770,34 @@ task("update-sublime", ["local", serverFile], function() {
jake.cpR(serverFile + ".map", "../TypeScript-Sublime-Plugin/tsserver/");
});

var tslintRuleDir = "scripts/tslint";
var tslintRules = ([
"nextLineRule",
"noInferrableTypesRule"
]);
var tslintRulesFiles = tslintRules.map(function(p) {
return path.join(tslintRuleDir, p + ".ts");
});
var tslintRulesOutFiles = tslintRules.map(function(p) {
return path.join(builtLocalDirectory, "tslint", p + ".js");
});
desc("Compiles tslint rules to js");
task("build-rules", tslintRulesOutFiles);
tslintRulesFiles.forEach(function(ruleFile, i) {
compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ true, /*noOutFile*/ true, /*generateDeclarations*/ false, path.join(builtLocalDirectory, "tslint"));
});

// if the codebase were free of linter errors we could make jake runtests
// run this task automatically
desc("Runs tslint on the compiler sources");
task("lint", [], function() {
task("lint", ["build-rules"], function() {
function success(f) { return function() { console.log('SUCCESS: No linter errors in ' + f + '\n'); }};
function failure(f) { return function() { console.log('FAILURE: Please fix linting errors in ' + f + '\n') }};

var lintTargets = compilerSources.concat(harnessCoreSources);
for (var i in lintTargets) {
var f = lintTargets[i];
var cmd = 'tslint -c tslint.json ' + f;
var cmd = 'tslint --rules-dir built/local/tslint -c tslint.json ' + f;
exec(cmd, success(f), failure(f));
}
}, { async: true });
63 changes: 63 additions & 0 deletions scripts/tslint/nextLineRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/// <reference path="../../lib/typescriptServices.d.ts" />
/// <reference path="../../node_modules/tslint/lib/tslint.d.ts" />

const OPTION_CATCH = "check-catch";
const OPTION_ELSE = "check-else";

export class Rule extends Lint.Rules.AbstractRule {
public static CATCH_FAILURE_STRING = "'catch' should be on the line following the previous block's ending curly brace";
public static ELSE_FAILURE_STRING = "'else' should be on the line following the previous block's ending curly brace";

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

class NextLineWalker extends Lint.RuleWalker {
public visitIfStatement(node: ts.IfStatement) {
const sourceFile = node.getSourceFile();
const thenStatement = node.thenStatement;

const elseStatement = node.elseStatement;
if (!!elseStatement) {
// find the else keyword
const elseKeyword = getFirstChildOfKind(node, ts.SyntaxKind.ElseKeyword);
if (this.hasOption(OPTION_ELSE) && !!elseKeyword) {
const thenStatementEndLoc = sourceFile.getLineAndCharacterOfPosition(thenStatement.getEnd());
const elseKeywordLoc = sourceFile.getLineAndCharacterOfPosition(elseKeyword.getStart());
if (thenStatementEndLoc.line !== (elseKeywordLoc.line - 1)) {
const failure = this.createFailure(elseKeyword.getStart(), elseKeyword.getWidth(), Rule.ELSE_FAILURE_STRING);
this.addFailure(failure);
}
}
}

super.visitIfStatement(node);
}

public visitTryStatement(node: ts.TryStatement) {
const sourceFile = node.getSourceFile();
const catchClause = node.catchClause;

// "visit" try block
const tryKeyword = node.getChildAt(0);
Copy link
Member

Choose a reason for hiding this comment

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

There's a getFirstToken()

const tryBlock = node.tryBlock;
const tryOpeningBrace = tryBlock.getChildAt(0);

if (this.hasOption(OPTION_CATCH) && !!catchClause) {
const tryClosingBrace = node.tryBlock.getChildAt(node.tryBlock.getChildCount() - 1);
Copy link
Member

Choose a reason for hiding this comment

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

getLastToken()

const catchKeyword = catchClause.getChildAt(0);
const tryClosingBraceLoc = sourceFile.getLineAndCharacterOfPosition(tryClosingBrace.getEnd());
const catchKeywordLoc = sourceFile.getLineAndCharacterOfPosition(catchKeyword.getStart());
if (tryClosingBraceLoc.line !== (catchKeywordLoc.line - 1)) {
const failure = this.createFailure(catchKeyword.getStart(), catchKeyword.getWidth(), Rule.CATCH_FAILURE_STRING);
this.addFailure(failure);
}
}
super.visitTryStatement(node);
}
}

function getFirstChildOfKind(node: ts.Node, kind: ts.SyntaxKind) {
return node.getChildren().filter((child) => child.kind === kind)[0];
}
32 changes: 32 additions & 0 deletions scripts/tslint/noInferrableTypesRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/// <reference path="../../lib/typescriptServices.d.ts" />
/// <reference path="../../node_modules/tslint/lib/tslint.d.ts" />


export class Rule extends Lint.Rules.AbstractRule {
public static FAILURE_STRING = "LHS type inferred by RHS expression, remove type annotation";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
const program = ts.createProgram([sourceFile.fileName], Lint.createCompilerOptions());
return this.applyWithWalker(new InferrableTypeWalker(sourceFile, this.getOptions(), program));
}
}

class InferrableTypeWalker extends Lint.RuleWalker {
constructor(file: ts.SourceFile, opts: Lint.IOptions, private program: ts.Program) {
super(program.getSourceFile(file.fileName), opts);
}

visitVariableStatement(node: ts.VariableStatement) {
node.declarationList.declarations.forEach(e => {
if (
(!!e.type) &&
(!!e.initializer) &&
(this.program.getTypeChecker().getTypeAtLocation(e.type) === this.program.getTypeChecker().getContextualType(e.initializer))
) {
this.addFailure(this.createFailure(e.type.getStart(), e.type.getWidth(), Rule.FAILURE_STRING));
}
});

super.visitVariableStatement(node);
}
}
7 changes: 7 additions & 0 deletions scripts/tslint/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"noImplicitAny": true,
"module": "commonjs",
"outDir": "../../built/local/tslint"
}
}
15 changes: 12 additions & 3 deletions tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"spaces"
],
"one-line": [true,
"check-open-brace"
"check-open-brace",
"check-whitespace"
],
"no-unreachable": true,
"no-use-before-declare": true,
Expand All @@ -21,14 +22,22 @@
"check-branch",
"check-operator",
"check-separator",
"check-type"
"check-type",
"check-module"
],
"typedef-whitespace": [true, {
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
}]
}],
"next-line": [true,
"check-catch",
"check-else"
],
"no-internal-module": true,
"no-trailing-whitespace": true,
"no-inferrable-types": true
}
}