Skip to content

Commit

Permalink
Added varnameuniqueness rule
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveSanderson authored and ashwinr committed Sep 15, 2013
1 parent d77b54e commit 8463bdd
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 0 deletions.
66 changes: 66 additions & 0 deletions bin/tslint-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -28098,6 +28098,71 @@ var Lint;
var Rules = Lint.Rules;
})(Lint || (Lint = {}));
var Lint;
(function (Lint) {
(function (Rules) {
var VarNameUniquenessRule = (function (_super) {
__extends(VarNameUniquenessRule, _super);
function VarNameUniquenessRule() {
_super.apply(this, arguments);
}
VarNameUniquenessRule.prototype.apply = function (syntaxTree) {
return this.applyWithWalker(new VarNameUniquenessWalker(syntaxTree));
};
VarNameUniquenessRule.FAILURE_STRING = "duplicate variable: '";
return VarNameUniquenessRule;
})(Rules.AbstractRule);
Rules.VarNameUniquenessRule = VarNameUniquenessRule;

var VarNameUniquenessWalker = (function (_super) {
__extends(VarNameUniquenessWalker, _super);
function VarNameUniquenessWalker(syntaxTree) {
_super.call(this, syntaxTree);

this.scopeStack = [new ScopeInfo()];
}
VarNameUniquenessWalker.prototype.visitNode = function (node) {
var isNewScope = this.isScopeBoundary(node);

if (isNewScope) {
this.scopeStack.push(new ScopeInfo());
}

_super.prototype.visitNode.call(this, node);

if (isNewScope) {
this.scopeStack.pop();
}
};

VarNameUniquenessWalker.prototype.visitVariableDeclarator = function (node) {
var identifier = node.identifier, variableName = identifier.text(), position = this.position() + identifier.leadingTriviaWidth(), currentScope = this.scopeStack[this.scopeStack.length - 1];

if (currentScope.variableNames.indexOf(variableName) >= 0) {
var failureString = VarNameUniquenessRule.FAILURE_STRING + variableName + "'";
this.addFailure(this.createFailure(position, identifier.width(), failureString));
} else {
currentScope.variableNames.push(variableName);
}

_super.prototype.visitVariableDeclarator.call(this, node);
};

VarNameUniquenessWalker.prototype.isScopeBoundary = function (node) {
return node instanceof TypeScript.FunctionDeclarationSyntax || node instanceof TypeScript.MemberFunctionDeclarationSyntax || node instanceof TypeScript.SimpleArrowFunctionExpressionSyntax || node instanceof TypeScript.ParenthesizedArrowFunctionExpressionSyntax;
};
return VarNameUniquenessWalker;
})(Lint.RuleWalker);

var ScopeInfo = (function () {
function ScopeInfo() {
this.variableNames = [];
}
return ScopeInfo;
})();
})(Lint.Rules || (Lint.Rules = {}));
var Rules = Lint.Rules;
})(Lint || (Lint = {}));
var Lint;
(function (Lint) {
(function (Rules) {
var OPTION_BRANCH = "check-branch";
Expand Down Expand Up @@ -28260,6 +28325,7 @@ var Lint;
"sub": Rules.SubRule.prototype,
"trailing": Rules.TrailingRule.prototype,
"varname": Rules.VarNameRule.prototype,
"varnameuniqueness": Rules.VarNameUniquenessRule.prototype,
"whitespace": Rules.WhitespaceRule.prototype
};

Expand Down
66 changes: 66 additions & 0 deletions lib/tslint.js
Original file line number Diff line number Diff line change
Expand Up @@ -28098,6 +28098,71 @@ var Lint;
var Rules = Lint.Rules;
})(Lint || (Lint = {}));
var Lint;
(function (Lint) {
(function (Rules) {
var VarNameUniquenessRule = (function (_super) {
__extends(VarNameUniquenessRule, _super);
function VarNameUniquenessRule() {
_super.apply(this, arguments);
}
VarNameUniquenessRule.prototype.apply = function (syntaxTree) {
return this.applyWithWalker(new VarNameUniquenessWalker(syntaxTree));
};
VarNameUniquenessRule.FAILURE_STRING = "duplicate variable: '";
return VarNameUniquenessRule;
})(Rules.AbstractRule);
Rules.VarNameUniquenessRule = VarNameUniquenessRule;

var VarNameUniquenessWalker = (function (_super) {
__extends(VarNameUniquenessWalker, _super);
function VarNameUniquenessWalker(syntaxTree) {
_super.call(this, syntaxTree);

this.scopeStack = [new ScopeInfo()];
}
VarNameUniquenessWalker.prototype.visitNode = function (node) {
var isNewScope = this.isScopeBoundary(node);

if (isNewScope) {
this.scopeStack.push(new ScopeInfo());
}

_super.prototype.visitNode.call(this, node);

if (isNewScope) {
this.scopeStack.pop();
}
};

VarNameUniquenessWalker.prototype.visitVariableDeclarator = function (node) {
var identifier = node.identifier, variableName = identifier.text(), position = this.position() + identifier.leadingTriviaWidth(), currentScope = this.scopeStack[this.scopeStack.length - 1];

if (currentScope.variableNames.indexOf(variableName) >= 0) {
var failureString = VarNameUniquenessRule.FAILURE_STRING + variableName + "'";
this.addFailure(this.createFailure(position, identifier.width(), failureString));
} else {
currentScope.variableNames.push(variableName);
}

_super.prototype.visitVariableDeclarator.call(this, node);
};

VarNameUniquenessWalker.prototype.isScopeBoundary = function (node) {
return node instanceof TypeScript.FunctionDeclarationSyntax || node instanceof TypeScript.MemberFunctionDeclarationSyntax || node instanceof TypeScript.SimpleArrowFunctionExpressionSyntax || node instanceof TypeScript.ParenthesizedArrowFunctionExpressionSyntax;
};
return VarNameUniquenessWalker;
})(Lint.RuleWalker);

var ScopeInfo = (function () {
function ScopeInfo() {
this.variableNames = [];
}
return ScopeInfo;
})();
})(Lint.Rules || (Lint.Rules = {}));
var Rules = Lint.Rules;
})(Lint || (Lint = {}));
var Lint;
(function (Lint) {
(function (Rules) {
var OPTION_BRANCH = "check-branch";
Expand Down Expand Up @@ -28260,6 +28325,7 @@ var Lint;
"sub": Rules.SubRule.prototype,
"trailing": Rules.TrailingRule.prototype,
"varname": Rules.VarNameRule.prototype,
"varnameuniqueness": Rules.VarNameUniquenessRule.prototype,
"whitespace": Rules.WhitespaceRule.prototype
};

Expand Down
2 changes: 2 additions & 0 deletions src/rules/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
/// <reference path='subRule.ts'/>
/// <reference path='trailingRule.ts'/>
/// <reference path='varNameRule.ts'/>
/// <reference path='varNameUniquenessRule.ts'/>
/// <reference path='whitespaceRule.ts'/>

module Lint.Rules {
Expand Down Expand Up @@ -70,6 +71,7 @@ module Lint.Rules {
"sub": SubRule.prototype,
"trailing": TrailingRule.prototype,
"varname": VarNameRule.prototype,
"varnameuniqueness": VarNameUniquenessRule.prototype,
"whitespace": WhitespaceRule.prototype
};

Expand Down
65 changes: 65 additions & 0 deletions src/rules/varNameUniquenessRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/// <reference path='rule.ts'/>
/// <reference path='abstractRule.ts'/>

module Lint.Rules {

export class VarNameUniquenessRule extends AbstractRule {
public static FAILURE_STRING = "duplicate variable: '";

public apply(syntaxTree: TypeScript.SyntaxTree): RuleFailure[] {
return this.applyWithWalker(new VarNameUniquenessWalker(syntaxTree));
}
}

class VarNameUniquenessWalker extends Lint.RuleWalker {
private scopeStack: ScopeInfo[];

constructor(syntaxTree: TypeScript.SyntaxTree) {
super(syntaxTree);

// initialize stack with global scope
this.scopeStack = [new ScopeInfo()];
}

public visitNode(node: TypeScript.SyntaxNode): void {
var isNewScope = this.isScopeBoundary(node);

if (isNewScope) {
this.scopeStack.push(new ScopeInfo());
}

super.visitNode(node);

if (isNewScope) {
this.scopeStack.pop();
}
}

public visitVariableDeclarator(node: TypeScript.VariableDeclaratorSyntax): void {
var identifier = node.identifier,
variableName = identifier.text(),
position = this.position() + identifier.leadingTriviaWidth(),
currentScope = this.scopeStack[this.scopeStack.length - 1];

if (currentScope.variableNames.indexOf(variableName) >= 0) {
var failureString = VarNameUniquenessRule.FAILURE_STRING + variableName + "'";
this.addFailure(this.createFailure(position, identifier.width(), failureString));
} else {
currentScope.variableNames.push(variableName);
}

super.visitVariableDeclarator(node);
}

private isScopeBoundary(node: TypeScript.SyntaxNode): boolean {
return node instanceof TypeScript.FunctionDeclarationSyntax
|| node instanceof TypeScript.MemberFunctionDeclarationSyntax
|| node instanceof TypeScript.SimpleArrowFunctionExpressionSyntax
|| node instanceof TypeScript.ParenthesizedArrowFunctionExpressionSyntax;
}
}

class ScopeInfo {
public variableNames: string[] = [];
}
}
26 changes: 26 additions & 0 deletions test/files/rules/varnameuniqueness.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
var duplicated = 1;

class Test {
private myFunc() {
var notDuplicated = 123,
duplicated = 234,
someFunc = () => {
var notDuplicated = 345;
};

var duplicated = null;
}
}

function test() {
var notDuplicated = 123,
duplicated = 234,
someFunc = () => {
var notDuplicated = 345;
};

var duplicated = null;
}

duplicated = 2;
var duplicated = 3;
18 changes: 18 additions & 0 deletions test/rules/varNameUniquenessRuleTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/// <reference path='../references.ts' />

describe("<varnameuniqueness>", () => {
it("ensures that variable declarations are unique within a scope", () => {
var fileName = "rules/varnameuniqueness.test.ts";
var failureString = Lint.Rules.VarNameUniquenessRule.FAILURE_STRING + "duplicated'";

var createFailure = Lint.Test.createFailuresOnFile(fileName, failureString);
var expectedFailures = [
createFailure([11, 13], [11, 23]),
createFailure([22, 9], [22, 19]),
createFailure([26, 5], [26, 15])
];

var actualFailures = Lint.Test.applyRuleOnFile(fileName, "varnameuniqueness");
Lint.Test.assertFailuresEqual(actualFailures, expectedFailures);
});
});
1 change: 1 addition & 0 deletions tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"sub": true,
"trailing": true,
"varname": true,
"varnameuniqueness": true,
"whitespace": [true,
"check-branch",
"check-decl",
Expand Down

0 comments on commit 8463bdd

Please sign in to comment.