diff --git a/src/rules/noEmptyRule.ts b/src/rules/noEmptyRule.ts index 09d6aa914bc..0f5525f7c08 100644 --- a/src/rules/noEmptyRule.ts +++ b/src/rules/noEmptyRule.ts @@ -21,9 +21,11 @@ import * as ts from "typescript"; import * as Lint from "../index"; const ALLOW_EMPTY_CATCH = "allow-empty-catch"; +const ALLOW_EMPTY_FUNCTIONS = "allow-empty-functions"; interface Options { allowEmptyCatch: boolean; + allowEmptyFunctions: boolean; } export class Rule extends Lint.Rules.AbstractRule { @@ -34,12 +36,29 @@ export class Rule extends Lint.Rules.AbstractRule { descriptionDetails: "Blocks with a comment inside are not considered empty.", rationale: "Empty blocks are often indicators of missing code.", optionsDescription: Lint.Utils.dedent` - If \`${ALLOW_EMPTY_CATCH}\` is specified, then catch blocks are allowed to be empty.`, + If \`${ALLOW_EMPTY_CATCH}\` is specified, then catch blocks are allowed to be empty. + If \`${ALLOW_EMPTY_FUNCTIONS}\` is specified, then function definitions are allowed to be empty.`, options: { - type: "string", - enum: [ALLOW_EMPTY_CATCH], + type: "array", + items: { + anyOf: [ + { + type: "string", + enum: [ALLOW_EMPTY_CATCH], + }, + { + type: "string", + enum: [ALLOW_EMPTY_FUNCTIONS], + }, + ], + }, }, - optionExamples: [true, [true, ALLOW_EMPTY_CATCH]], + optionExamples: [ + true, + [true, ALLOW_EMPTY_CATCH], + [true, ALLOW_EMPTY_FUNCTIONS], + [true, ALLOW_EMPTY_CATCH, ALLOW_EMPTY_FUNCTIONS], + ], type: "functionality", typescriptOnly: false, }; @@ -50,6 +69,7 @@ export class Rule extends Lint.Rules.AbstractRule { public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] { return this.applyWithFunction(sourceFile, walk, { allowEmptyCatch: this.ruleArguments.indexOf(ALLOW_EMPTY_CATCH) !== -1, + allowEmptyFunctions: this.ruleArguments.indexOf(ALLOW_EMPTY_FUNCTIONS) !== -1, }); } } @@ -76,6 +96,13 @@ function isExcluded(node: ts.Node, options: Options): boolean { return true; } + if (options.allowEmptyFunctions && + (node.kind === ts.SyntaxKind.FunctionDeclaration || + node.kind === ts.SyntaxKind.FunctionExpression || + node.kind === ts.SyntaxKind.ArrowFunction)) { + return true; + } + return isConstructorDeclaration(node) && ( /* If constructor is private or protected, the block is allowed to be empty. diff --git a/test/rules/no-empty/allow-empty-catch/test.ts.lint b/test/rules/no-empty/allow-empty-catch/test.ts.lint index 407cfe58ebb..a0f872ee905 100644 --- a/test/rules/no-empty/allow-empty-catch/test.ts.lint +++ b/test/rules/no-empty/allow-empty-catch/test.ts.lint @@ -9,13 +9,19 @@ if (x === 2) { } ~ [block is empty] -function testFunction() { - ~ +function testFunction1() { + ~ ~nil } ~ [block is empty] +const testFunction2 = () => { }; + ~~~ [block is empty] + +const testFunction3 = function() {} + ~~ [block is empty] + for (var x = 0; x < 1; ++x) { } ~~~ [block is empty] diff --git a/test/rules/no-empty/allow-empty-functions/test.ts.lint b/test/rules/no-empty/allow-empty-functions/test.ts.lint new file mode 100644 index 00000000000..2c5b73430f9 --- /dev/null +++ b/test/rules/no-empty/allow-empty-functions/test.ts.lint @@ -0,0 +1,73 @@ +if (x === 1) {} + ~~ [block is empty] +if (x === 2) { + ~ + +~nil + +~nil +} +~ [block is empty] + +function testFunction1() { + + + +} + +const testFunction2 = () => { }; + +const testFunction3 = function() {} + +for (var x = 0; x < 1; ++x) { } + ~~~ [block is empty] + +// empty blocks with comments should be legal +for (var y = 0; y < 1; ++y) { + // empty here +} +{ // empty block allowed +} +{ + /* this block is also empty, but allowed to be */ +} + +class testClass { + constructor(private allowed: any, private alsoAllowed: any) { + } +} + +class testClass2 { + constructor(protected allowed: any) { + } +} + +class testClass3 { + constructor(notAllowed: any) { + ~ + } +~~~~~ [block is empty] +} + +class testClass4 { + constructor(readonly allowed: any) { + } +} + +class PrivateClassConstructor { + private constructor() {} +} + +class ProtectedClassConstructor { + protected constructor() {} +} + +class PublicClassConstructor { + public constructor() {} + ~~ [block is empty] +} + +try { + throw new Error(); +} catch (error) {} + ~~ [block is empty] diff --git a/test/rules/no-empty/allow-empty-functions/tslint.json b/test/rules/no-empty/allow-empty-functions/tslint.json new file mode 100644 index 00000000000..56754e50512 --- /dev/null +++ b/test/rules/no-empty/allow-empty-functions/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "no-empty": [true, "allow-empty-functions"] + } +} diff --git a/test/rules/no-empty/default/test.ts.lint b/test/rules/no-empty/default/test.ts.lint index ea947abfc3a..de225995e76 100644 --- a/test/rules/no-empty/default/test.ts.lint +++ b/test/rules/no-empty/default/test.ts.lint @@ -9,13 +9,19 @@ if (x === 2) { } ~ [block is empty] -function testFunction() { - ~ +function testFunction1() { + ~ ~nil } ~ [block is empty] +const testFunction2 = () => { }; + ~~~ [block is empty] + +const testFunction3 = function() {} + ~~ [block is empty] + for (var x = 0; x < 1; ++x) { } ~~~ [block is empty]