Skip to content

Commit

Permalink
Add new rule adjacent-overload-signatures (palantir#1426)
Browse files Browse the repository at this point in the history
  • Loading branch information
Raghav Katyal authored and adidahiya committed Jul 28, 2016
1 parent 965edda commit 53b0108
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ Core Rules

Core rules are included in the `tslint` package.

* `adjacent-overload-signatures` enforces function overloads to be consecutive.
* `align` enforces vertical alignment. Rule options:
* `"parameters"` checks alignment of function parameters.
* `"arguments"` checks alignment of function call arguments.
Expand Down
93 changes: 93 additions & 0 deletions src/rules/adjacentOverloadSignaturesRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* @license
* Copyright 2013 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 "../lint";

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"],
type: "typescript",
};
/* tslint:enable:object-literal-sort-keys */

public static FAILURE_STRING_FACTORY = (name: string) => `All '${name}' signatures should be adjacent`;

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

class AdjacentOverloadSignaturesWalker extends Lint.RuleWalker {

public visitInterfaceDeclaration(node: ts.InterfaceDeclaration): void {
this.checkNode(node);
super.visitInterfaceDeclaration(node);
}

public visitTypeLiteral(node: ts.TypeLiteralNode): void {
this.checkNode(node);
super.visitTypeLiteral(node);
}

public checkNode(node: ts.TypeLiteralNode | ts.InterfaceDeclaration) {
let last: string = undefined;
const seen: { [name: string]: boolean } = {};
for (const member of node.members) {
if (member.name !== undefined) {
const methodName = getTextOfPropertyName(member.name);
if (methodName !== undefined) {
if (seen[methodName] && last !== methodName) {
this.addFailure(this.createFailure(member.getStart(), member.getWidth(),
Rule.FAILURE_STRING_FACTORY(methodName)));
}
last = methodName;
seen[methodName] = true;
}
} else {
last = undefined;
}
}
}
}

function isLiteralExpression(node: ts.Node): node is ts.LiteralExpression {
return node.kind === ts.SyntaxKind.StringLiteral || node.kind === ts.SyntaxKind.NumericLiteral;
}

function getTextOfPropertyName(name: ts.PropertyName): string {
switch (name.kind) {
case ts.SyntaxKind.Identifier:
return (name as ts.Identifier).text;
case ts.SyntaxKind.ComputedPropertyName:
const { expression } = (name as ts.ComputedPropertyName);
if (isLiteralExpression(expression)) {
return expression.text;
}
default:
if (isLiteralExpression(name)) {
return name.text;
}
}
}
1 change: 1 addition & 0 deletions src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"language/walker/scopeAwareRuleWalker.ts",
"language/walker/skippableTokenAwareRuleWalker.ts",
"language/walker/syntaxWalker.ts",
"rules/adjacentOverloadSignaturesRule.ts",
"rules/alignRule.ts",
"rules/arrowParensRule.ts",
"rules/banRule.ts",
Expand Down
80 changes: 80 additions & 0 deletions test/rules/adjacent-overload-signatures/test.ts.lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// good

interface i1 {
a();
a(x: number);
b();
b(x: string);
}

interface i2 {
a();
a(x: number);
a(): void;
b();
b(x: string);
}

interface i3 {
a();
"a"();
}

interface i4 {
a();
["a"]();
}

interface i5 {
a(): string;
bar: {
a(): number;
}
}


// bad

interface b1 {
a();
a(x: number);
b();
b(x: string);
a(x: string);
~~~~~~~~~~~~~ [All 'a' signatures should be adjacent]
}

interface b2 {
a();
a(x: number);
b();
b(x: string);
a(): void;
~~~~~~~~~~ [All 'a' signatures should be adjacent]
}

interface b3 {
a();
12();
"a"();
~~~~~~ [All 'a' signatures should be adjacent]
12();
~~~~~ [All '12' signatures should be adjacent]
}

interface b4 {
a();
b(): void;
["a"](v: number): void;
~~~~~~~~~~~~~~~~~~~~~~~ [All 'a' signatures should be adjacent]
}

interface b5 {
a(): string;
bar: {
a(): number;
b();
a(b: number): void;
~~~~~~~~~~~~~~~~~~~ [All 'a' signatures should be adjacent]
}
}
5 changes: 5 additions & 0 deletions test/rules/adjacent-overload-signatures/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"rules": {
"adjacent-overload-signatures": true
}
}
1 change: 1 addition & 0 deletions test/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"../src/lint.ts",
"../src/ruleLoader.ts",
"../src/rules.ts",
"../src/rules/adjacentOverloadSignaturesRule.ts",
"../src/rules/alignRule.ts",
"../src/rules/arrowParensRule.ts",
"../src/rules/banRule.ts",
Expand Down

0 comments on commit 53b0108

Please sign in to comment.