-
Notifications
You must be signed in to change notification settings - Fork 12.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4458 from weswigham/tslint-rules
Add tslint rules for #3994
- Loading branch information
Showing
7 changed files
with
222 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/// <reference path="../../node_modules/tslint/typings/typescriptServices.d.ts" /> | ||
/// <reference path="../../node_modules/tslint/lib/tslint.d.ts" /> | ||
|
||
|
||
export class Rule extends Lint.Rules.AbstractRule { | ||
public static FAILURE_STRING_FACTORY = (name: string, currently: string) => `Tag boolean argument as '${name}' (currently '${currently}')`; | ||
|
||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { | ||
const program = ts.createProgram([sourceFile.fileName], Lint.createCompilerOptions()); | ||
const checker = program.getTypeChecker(); | ||
return this.applyWithWalker(new BooleanTriviaWalker(checker, program.getSourceFile(sourceFile.fileName), this.getOptions())); | ||
} | ||
} | ||
|
||
class BooleanTriviaWalker extends Lint.RuleWalker { | ||
constructor(private checker: ts.TypeChecker, file: ts.SourceFile, opts: Lint.IOptions) { | ||
super(file, opts); | ||
} | ||
|
||
visitCallExpression(node: ts.CallExpression) { | ||
super.visitCallExpression(node); | ||
if (node.arguments) { | ||
const targetCallSignature = this.checker.getResolvedSignature(node); | ||
if (!!targetCallSignature) { | ||
const targetParameters = targetCallSignature.getParameters(); | ||
const source = this.getSourceFile(); | ||
for (let index = 0; index < targetParameters.length; index++) { | ||
const param = targetParameters[index]; | ||
const arg = node.arguments[index]; | ||
if (!(arg && param)) continue; | ||
|
||
const argType = this.checker.getContextualType(arg); | ||
if (argType && (argType.getFlags() & ts.TypeFlags.Boolean)) { | ||
if (arg.kind !== ts.SyntaxKind.TrueKeyword && arg.kind !== ts.SyntaxKind.FalseKeyword) { | ||
continue; | ||
} | ||
let triviaContent: string; | ||
const ranges = ts.getLeadingCommentRanges(arg.getFullText(), 0); | ||
if (ranges && ranges.length === 1 && ranges[0].kind === ts.SyntaxKind.MultiLineCommentTrivia) { | ||
triviaContent = arg.getFullText().slice(ranges[0].pos + 2, ranges[0].end - 2); //+/-2 to remove /**/ | ||
} | ||
if (triviaContent !== param.getName()) { | ||
this.addFailure(this.createFailure(arg.getStart(source), arg.getWidth(source), Rule.FAILURE_STRING_FACTORY(param.getName(), triviaContent))); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/// <reference path="../../node_modules/tslint/typings/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(sourceFile)); | ||
if (thenStatementEndLoc.line !== (elseKeywordLoc.line - 1)) { | ||
const failure = this.createFailure(elseKeyword.getStart(sourceFile), elseKeyword.getWidth(sourceFile), 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 tryBlock = node.tryBlock; | ||
|
||
if (this.hasOption(OPTION_CATCH) && !!catchClause) { | ||
const tryClosingBrace = tryBlock.getLastToken(sourceFile); | ||
const catchKeyword = catchClause.getFirstToken(sourceFile); | ||
const tryClosingBraceLoc = sourceFile.getLineAndCharacterOfPosition(tryClosingBrace.getEnd()); | ||
const catchKeywordLoc = sourceFile.getLineAndCharacterOfPosition(catchKeyword.getStart(sourceFile)); | ||
if (tryClosingBraceLoc.line !== (catchKeywordLoc.line - 1)) { | ||
const failure = this.createFailure(catchKeyword.getStart(sourceFile), catchKeyword.getWidth(sourceFile), 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]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/// <reference path="../../node_modules/tslint/typings/typescriptServices.d.ts" /> | ||
/// <reference path="../../node_modules/tslint/lib/tslint.d.ts" /> | ||
|
||
|
||
export class Rule extends Lint.Rules.AbstractRule { | ||
public static FAILURE_STRING_FACTORY = (type: string) => `LHS type (${type}) inferred by RHS expression, remove type annotation`; | ||
|
||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { | ||
return this.applyWithWalker(new InferrableTypeWalker(sourceFile, this.getOptions())); | ||
} | ||
} | ||
|
||
class InferrableTypeWalker extends Lint.RuleWalker { | ||
visitVariableStatement(node: ts.VariableStatement) { | ||
node.declarationList.declarations.forEach(e => { | ||
if ((!!e.type) && (!!e.initializer)) { | ||
let failure: string; | ||
switch (e.type.kind) { | ||
case ts.SyntaxKind.BooleanKeyword: | ||
if (e.initializer.kind === ts.SyntaxKind.TrueKeyword || e.initializer.kind === ts.SyntaxKind.FalseKeyword) { | ||
failure = 'boolean'; | ||
} | ||
break; | ||
case ts.SyntaxKind.NumberKeyword: | ||
if (e.initializer.kind === ts.SyntaxKind.NumericLiteral) { | ||
failure = 'number'; | ||
} | ||
break; | ||
case ts.SyntaxKind.StringKeyword: | ||
switch (e.initializer.kind) { | ||
case ts.SyntaxKind.StringLiteral: | ||
case ts.SyntaxKind.NoSubstitutionTemplateLiteral: | ||
case ts.SyntaxKind.TemplateExpression: | ||
failure = 'string'; | ||
break; | ||
default: | ||
break; | ||
} | ||
break; | ||
} | ||
if (failure) { | ||
this.addFailure(this.createFailure(e.type.getStart(), e.type.getWidth(), Rule.FAILURE_STRING_FACTORY(failure))); | ||
} | ||
} | ||
}); | ||
|
||
super.visitVariableStatement(node); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/// <reference path="../../node_modules/tslint/typings/typescriptServices.d.ts" /> | ||
/// <reference path="../../node_modules/tslint/lib/tslint.d.ts" /> | ||
|
||
|
||
export class Rule extends Lint.Rules.AbstractRule { | ||
public static FAILURE_STRING = "Don't use the 'null' keyword - use 'undefined' for missing values instead"; | ||
|
||
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { | ||
return this.applyWithWalker(new NullWalker(sourceFile, this.getOptions())); | ||
} | ||
} | ||
|
||
class NullWalker extends Lint.RuleWalker { | ||
visitNode(node: ts.Node) { | ||
super.visitNode(node); | ||
if (node.kind === ts.SyntaxKind.NullKeyword) { | ||
this.addFailure(this.createFailure(node.getStart(), node.getWidth(), Rule.FAILURE_STRING)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"compilerOptions": { | ||
"noImplicitAny": true, | ||
"module": "commonjs", | ||
"outDir": "../../built/local/tslint" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters