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

[new-rule] Added new invalid-void rule #4736

Merged
merged 15 commits into from Jul 6, 2019
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
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
1 change: 1 addition & 0 deletions src/configs/all.ts
Expand Up @@ -163,6 +163,7 @@ export const rules = {
"cyclomatic-complexity": true,
eofline: true,
indent: { options: ["spaces"] },
"invalid-void": true,
"linebreak-style": { options: "LF" },
"max-classes-per-file": { options: 1 },
"max-file-line-count": { options: 1000 },
Expand Down
6 changes: 4 additions & 2 deletions src/language/rule/abstractRule.ts
Expand Up @@ -67,11 +67,13 @@ export abstract class AbstractRule implements IRule {
): RuleFailure[];
protected applyWithFunction<T, U>(
sourceFile: ts.SourceFile,
walkFn: (ctx: WalkContext<T | void>, programOrChecker?: U) => void,
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
walkFn: (ctx: WalkContext<T> | WalkContext, programOrChecker?: U) => void,
options?: T,
programOrChecker?: U,
): RuleFailure[] {
const ctx = new WalkContext(sourceFile, this.ruleName, options);
const ctx = new WalkContext(sourceFile, this.ruleName, options) as (
| WalkContext<T>
| WalkContext);
walkFn(ctx, programOrChecker);
return ctx.failures;
}
Expand Down
2 changes: 1 addition & 1 deletion src/language/walker/walkContext.ts
Expand Up @@ -19,7 +19,7 @@ import * as ts from "typescript";

import { Fix, RuleFailure } from "../rule/rule";

export class WalkContext<T = void> {
export class WalkContext<T = {}> {
public readonly failures: RuleFailure[] = [];

constructor(
Expand Down
2 changes: 1 addition & 1 deletion src/language/walker/walker.ts
Expand Up @@ -27,7 +27,7 @@ export interface IWalker {
getFailures(): RuleFailure[];
}

export abstract class AbstractWalker<T = void> extends WalkContext<T> implements IWalker {
export abstract class AbstractWalker<T = {}> extends WalkContext<T> implements IWalker {
public abstract walk(sourceFile: ts.SourceFile): void;

public getSourceFile() {
Expand Down
2 changes: 1 addition & 1 deletion src/rules/deprecationRule.ts
Expand Up @@ -58,7 +58,7 @@ export class Rule extends Lint.Rules.TypedRule {
}

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, {}, program.getTypeChecker());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rules/importSpacingRule.ts
Expand Up @@ -45,7 +45,7 @@ export class Rule extends Lint.Rules.AbstractRule {
public static NO_LINE_BREAKS = "Line breaks are not allowed in import declaration";

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

Expand Down
86 changes: 86 additions & 0 deletions src/rules/invalidVoidRule.ts
@@ -0,0 +1,86 @@
/**
* @license
* Copyright 2019 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as ts from "typescript";

import * as Lint from "../index";

export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
ruleName: "invalid-void",
description: Lint.Utils.dedent`
Disallows usage of \`void\` type outside of return type.
If \`void\` is used as return type, it shouldn't be a part of intersection/union type.`,
rationale: Lint.Utils.dedent`
The \`void\` type means "nothing" or that a function does not return any value,
in contra with implicit \`undefined\` type which means that a function returns a value \`undefined\`.
So "nothing" cannot be mixed with any other types.
If you need this - use \`undefined\` type instead.`,
hasFix: false,
optionsDescription: "Not configurable.",
options: null,
optionExamples: [true],
type: "maintainability",
typescriptOnly: true,
};
/* tslint:enable:object-literal-sort-keys */

public static FAILURE_STRING = "void is not a valid type other than return types";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk);
}
}

const failedKinds = new Set([
ts.SyntaxKind.PropertySignature,
ts.SyntaxKind.PropertyDeclaration,

ts.SyntaxKind.VariableDeclaration,
ts.SyntaxKind.TypeAliasDeclaration,

ts.SyntaxKind.IntersectionType,
ts.SyntaxKind.UnionType,

ts.SyntaxKind.Parameter,
ts.SyntaxKind.TypeParameter,

ts.SyntaxKind.AsExpression,
ts.SyntaxKind.TypeAssertionExpression,

ts.SyntaxKind.TypeOperator,
ts.SyntaxKind.ArrayType,

ts.SyntaxKind.MappedType,
ts.SyntaxKind.ConditionalType,

ts.SyntaxKind.TypeReference,

ts.SyntaxKind.NewExpression,
ts.SyntaxKind.CallExpression,
]);

function walk(ctx: Lint.WalkContext): void {
ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node) {
if (node.kind === ts.SyntaxKind.VoidKeyword && failedKinds.has(node.parent.kind)) {
ctx.addFailureAtNode(node, Rule.FAILURE_STRING);
}

ts.forEachChild(node, cb);
});
}
2 changes: 1 addition & 1 deletion src/rules/matchDefaultExportNameRule.ts
Expand Up @@ -41,7 +41,7 @@ export class Rule extends Lint.Rules.TypedRule {
}

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, {}, program.getTypeChecker());
}
}

Expand Down
4 changes: 1 addition & 3 deletions src/rules/newlineBeforeReturnRule.ts
Expand Up @@ -39,9 +39,7 @@ export class Rule extends Lint.Rules.AbstractRule {
public static FAILURE_STRING = "Missing blank line before return";

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

Expand Down
4 changes: 1 addition & 3 deletions src/rules/newlinePerChainedCallRule.ts
Expand Up @@ -42,9 +42,7 @@ export class Rule extends Lint.Rules.AbstractRule {
public static FAILURE_STRING = "When chaining calls, put method calls on new lines.";

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

Expand Down
2 changes: 1 addition & 1 deletion src/rules/noBooleanLiteralCompareRule.ts
Expand Up @@ -46,7 +46,7 @@ export class Rule extends Lint.Rules.TypedRule {
}

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, {}, program.getTypeChecker());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rules/noForInArrayRule.ts
Expand Up @@ -51,7 +51,7 @@ export class Rule extends Lint.Rules.TypedRule {
"for-in loops over arrays are forbidden. Use for-of or array.forEach instead.";

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, {}, program.getTypeChecker());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rules/noInferredEmptyObjectTypeRule.ts
Expand Up @@ -59,7 +59,7 @@ class NoInferredEmptyObjectTypeRule extends Lint.AbstractWalker {
ruleName: string,
private readonly checker: ts.TypeChecker,
) {
super(sourceFile, ruleName, undefined);
super(sourceFile, ruleName, {});
}

public walk(sourceFile: ts.SourceFile) {
Expand Down
4 changes: 1 addition & 3 deletions src/rules/noInternalModuleRule.ts
Expand Up @@ -40,9 +40,7 @@ export class Rule extends Lint.Rules.AbstractRule {
"The internal 'module' syntax is deprecated, use the 'namespace' keyword instead.";

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

Expand Down
2 changes: 1 addition & 1 deletion src/rules/noMergeableNamespaceRule.ts
Expand Up @@ -37,7 +37,7 @@ export class Rule extends Lint.Rules.AbstractRule {
}

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

Expand Down
2 changes: 1 addition & 1 deletion src/rules/noNullUndefinedUnionRule.ts
Expand Up @@ -46,7 +46,7 @@ export class Rule extends Lint.Rules.TypedRule {
public static FAILURE_STRING = "Union type cannot include both 'null' and 'undefined'.";

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, {}, program.getTypeChecker());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rules/noSwitchCaseFallThroughRule.ts
Expand Up @@ -65,7 +65,7 @@ export class Rule extends Lint.Rules.AbstractRule {

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(
new NoSwitchCaseFallThroughWalker(sourceFile, this.ruleName, undefined),
new NoSwitchCaseFallThroughWalker(sourceFile, this.ruleName, {}),
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/rules/noUnnecessaryQualifierRule.ts
Expand Up @@ -40,7 +40,7 @@ export class Rule extends Lint.Rules.TypedRule {
}

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, {}, program.getTypeChecker());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rules/noUnsafeAnyRule.ts
Expand Up @@ -71,7 +71,7 @@ class NoUnsafeAnyWalker extends Lint.AbstractWalker {
ruleName: string,
private readonly checker: ts.TypeChecker,
) {
super(sourceFile, ruleName, undefined);
super(sourceFile, ruleName, {});
}

public walk(sourceFile: ts.SourceFile) {
Expand Down
2 changes: 1 addition & 1 deletion src/rules/noUseBeforeDeclareRule.ts
Expand Up @@ -54,7 +54,7 @@ export class Rule extends Lint.Rules.TypedRule {
}

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, {}, program.getTypeChecker());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rules/restrictPlusOperandsRule.ts
Expand Up @@ -40,7 +40,7 @@ export class Rule extends Lint.Rules.TypedRule {
public static SUGGEST_TEMPLATE_LITERALS = ". Consider using template literals.";

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, {}, program.getTypeChecker());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rules/returnUndefinedRule.ts
Expand Up @@ -48,7 +48,7 @@ export class Rule extends Lint.Rules.TypedRule {
"`void` function should use `return;`, not `return undefined;`.";

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, {}, program.getTypeChecker());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rules/strictTypePredicatesRule.ts
Expand Up @@ -68,7 +68,7 @@ export class Rule extends Lint.Rules.TypedRule {
showWarningOnce("strict-type-predicates does not work without --strictNullChecks");
return [];
}
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, {}, program.getTypeChecker());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rules/unnecessaryBindRule.ts
Expand Up @@ -47,7 +47,7 @@ export class Rule extends Lint.Rules.OptionallyTypedRule {
}

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, {}, program.getTypeChecker());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/rules/useDefaultTypeParameterRule.ts
Expand Up @@ -45,7 +45,7 @@ export class Rule extends Lint.Rules.TypedRule {
"This is the default value for this type parameter, so it can be omitted.";

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk, undefined, program.getTypeChecker());
return this.applyWithFunction(sourceFile, walk, {}, program.getTypeChecker());
}
}

Expand Down