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

Commit

Permalink
Add option to ignore accessors in adjacent-overload-signatures (#3718)
Browse files Browse the repository at this point in the history
* add noAsyncWithoutAwait rule

* fix lint

* code review updates

* Add option to ignore accessors in adjacent-overload-signatures

* Fix options schema

* allow return as well as await

* remove unneeded lint ignore

* improve rationale & fix performance issue

* fixes according to review

* finish fixing according to review

* Initial feedback cleanups

* Refactored to walk function

* Reset tslint.json to master

* Converted options to object form

* Disabled no-object-literal-type-assertion complaint
  • Loading branch information
saberduck authored and Josh Goldberg committed Jun 16, 2019
1 parent 0807692 commit 20eec28
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 12 deletions.
60 changes: 48 additions & 12 deletions src/rules/adjacentOverloadSignaturesRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,30 @@ import * as ts from "typescript";

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

const OPTION_IGNORE_ACCESSORS = "ignore-accessors";

interface Options {
ignoreAccessors: boolean;
}

export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
ruleName: "adjacent-overload-signatures",
description: "Enforces function overloads to be consecutive.",
optionsDescription: "Not configurable.",
options: null,
optionExamples: [true],
optionsDescription: Lint.Utils.dedent`
If \`${OPTION_IGNORE_ACCESSORS}\` is specified, then getters and setters are not considered to be overloads
of function with the same signature.`,
options: {
type: "object",
properties: {
[OPTION_IGNORE_ACCESSORS]: {
type: "boolean",
},
},
additionalProperties: false,
},
optionExamples: [true, [true, { OPTION_IGNORE_ACCESSORS: true }]],
rationale:
"Improves readability and organization by grouping naturally related items together.",
type: "typescript",
Expand All @@ -40,11 +56,15 @@ export class Rule extends Lint.Rules.AbstractRule {
}

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk);
// tslint:disable-next-line: no-object-literal-type-assertion
const rawOptions = { ...this.ruleArguments[0] } as { [OPTION_IGNORE_ACCESSORS]?: boolean };
return this.applyWithFunction(sourceFile, walk, {
ignoreAccessors: !!rawOptions[OPTION_IGNORE_ACCESSORS],
});
}
}

function walk(ctx: Lint.WalkContext): void {
function walk(ctx: Lint.WalkContext<Options>): void {
const { sourceFile } = ctx;
visitStatements(sourceFile.statements);
return ts.forEachChild(sourceFile, function cb(node: ts.Node): void {
Expand All @@ -61,8 +81,13 @@ function walk(ctx: Lint.WalkContext): void {
| ts.ClassDeclaration
| ts.TypeLiteralNode;
addFailures(
getMisplacedOverloads<ts.TypeElement | ts.ClassElement>(members, member =>
utils.isSignatureDeclaration(member) ? getOverloadKey(member) : undefined,
getMisplacedOverloads<ts.TypeElement | ts.ClassElement>(
members,
member =>
utils.isSignatureDeclaration(member)
? getOverloadKey(member)
: undefined,
ctx.options.ignoreAccessors,
),
);
}
Expand All @@ -73,10 +98,13 @@ function walk(ctx: Lint.WalkContext): void {

function visitStatements(statements: ReadonlyArray<ts.Statement>): void {
addFailures(
getMisplacedOverloads(statements, statement =>
utils.isFunctionDeclaration(statement) && statement.name !== undefined
? statement.name.text
: undefined,
getMisplacedOverloads(
statements,
statement =>
utils.isFunctionDeclaration(statement) && statement.name !== undefined
? statement.name.text
: undefined,
ctx.options.ignoreAccessors,
),
);
}
Expand All @@ -92,12 +120,16 @@ function walk(ctx: Lint.WalkContext): void {
function getMisplacedOverloads<T extends ts.Node>(
overloads: ReadonlyArray<T>,
getKey: (node: T) => string | undefined,
ignoreAccessors: boolean,
): ts.SignatureDeclaration[] {
const result: ts.SignatureDeclaration[] = [];
let lastKey: string | undefined;
const seen = new Set<string>();
for (const node of overloads) {
if (node.kind === ts.SyntaxKind.SemicolonClassElement) {
if (
node.kind === ts.SyntaxKind.SemicolonClassElement ||
(ignoreAccessors && isAccessor(node))
) {
continue;
}

Expand All @@ -115,6 +147,10 @@ function getMisplacedOverloads<T extends ts.Node>(
return result;
}

function isAccessor(member: ts.Node): boolean {
return member.kind === ts.SyntaxKind.GetAccessor || member.kind === ts.SyntaxKind.SetAccessor;
}

function printOverload(node: ts.SignatureDeclaration): string {
const info = getOverloadInfo(node);
return typeof info === "string" ? info : info === undefined ? "<unknown>" : info.name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,14 @@ interface I {
[Symbol.iterator](): void;
~~~~~~~~~~~~~~~~~~~~~~~~~~ [All 'Symbol.iterator' signatures should be adjacent]
}

class Accessors {
private x: number;
private y: number;
get x() {return this.x;}
get y() {return this.y;}
set x(newX: number) {this.x = newX;}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [All 'x' signatures should be adjacent]
set y(newY: number) {this.y = newY;}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [All 'y' signatures should be adjacent]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// good

class Accessors {
private x: number;
private y: number;
get x() {return this.x;}
get y() {return this.y;}
// setter is not considered as an overload of getter
set x(newX: number) {this.x = newX;}
set y(newY: number) {this.y = newY;}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"adjacent-overload-signatures": [true, { "ignore-accessors": true }]
}
}

0 comments on commit 20eec28

Please sign in to comment.