diff options
Diffstat (limited to 'node_modules/tslint/lib/rules/noShadowedVariableRule.js')
-rw-r--r-- | node_modules/tslint/lib/rules/noShadowedVariableRule.js | 328 |
1 files changed, 235 insertions, 93 deletions
diff --git a/node_modules/tslint/lib/rules/noShadowedVariableRule.js b/node_modules/tslint/lib/rules/noShadowedVariableRule.js index 53b3db09d..7aa485c18 100644 --- a/node_modules/tslint/lib/rules/noShadowedVariableRule.js +++ b/node_modules/tslint/lib/rules/noShadowedVariableRule.js @@ -17,8 +17,7 @@ */ Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); -// tslint:disable deprecation -// (https://github.com/palantir/tslint/pull/2598) +var tsutils_1 = require("tsutils"); var ts = require("typescript"); var Lint = require("../index"); var Rule = (function (_super) { @@ -28,116 +27,259 @@ var Rule = (function (_super) { } /* tslint:enable:object-literal-sort-keys */ Rule.FAILURE_STRING_FACTORY = function (name) { - return "Shadowed variable: '" + name + "'"; + return "Shadowed name: '" + name + "'"; }; Rule.prototype.apply = function (sourceFile) { - return this.applyWithWalker(new NoShadowedVariableWalker(sourceFile, this.getOptions())); + return this.applyWithWalker(new NoShadowedVariableWalker(sourceFile, this.ruleName, parseOptions(this.ruleArguments[0]))); + }; + /* tslint:disable:object-literal-sort-keys */ + Rule.metadata = { + ruleName: "no-shadowed-variable", + description: "Disallows shadowing variable declarations.", + rationale: "Shadowing a variable masks access to it and obscures to what value an identifier actually refers.", + optionsDescription: (_a = ["\n You can optionally pass an object to disable checking for certain kinds of declarations.\n Possible keys are `\"class\"`, `\"enum\"`, `\"function\"`, `\"import\"`, `\"interface\"`, `\"namespace\"`, `\"typeAlias\"`\n and `\"typeParameter\"`. Just set the value to `false` for the check you want to disable.\n All checks default to `true`, i.e. are enabled by default.\n Not that you cannot disable variables and parameters.\n "], _a.raw = ["\n You can optionally pass an object to disable checking for certain kinds of declarations.\n Possible keys are \\`\"class\"\\`, \\`\"enum\"\\`, \\`\"function\"\\`, \\`\"import\"\\`, \\`\"interface\"\\`, \\`\"namespace\"\\`, \\`\"typeAlias\"\\`\n and \\`\"typeParameter\"\\`. Just set the value to \\`false\\` for the check you want to disable.\n All checks default to \\`true\\`, i.e. are enabled by default.\n Not that you cannot disable variables and parameters.\n "], Lint.Utils.dedent(_a)), + options: { + type: "object", + properties: { + class: { type: "boolean" }, + enum: { type: "boolean" }, + function: { type: "boolean" }, + import: { type: "boolean" }, + interface: { type: "boolean" }, + namespace: { type: "boolean" }, + typeAlias: { type: "boolean" }, + typeParameter: { type: "boolean" }, + }, + }, + optionExamples: [ + true, + [true, { class: true, enum: true, function: true, interface: false, namespace: true, typeAlias: false, typeParameter: false }], + ], + type: "functionality", + typescriptOnly: false, }; return Rule; }(Lint.Rules.AbstractRule)); -/* tslint:disable:object-literal-sort-keys */ -Rule.metadata = { - ruleName: "no-shadowed-variable", - description: "Disallows shadowing variable declarations.", - rationale: "Shadowing a variable masks access to it and obscures to what value an identifier actually refers.", - optionsDescription: "Not configurable.", - options: null, - optionExamples: [true], - type: "functionality", - typescriptOnly: false, -}; exports.Rule = Rule; +function parseOptions(option) { + return tslib_1.__assign({ class: true, enum: true, function: true, import: true, interface: true, namespace: true, typeAlias: true, typeParameter: true }, option); +} +var Scope = (function () { + function Scope(functionScope) { + this.variables = new Map(); + this.variablesSeen = new Map(); + this.reassigned = new Set(); + // if no functionScope is provided we are in the process of creating a new function scope, which for consistency links to itself + this.functionScope = functionScope !== undefined ? functionScope : this; + } + Scope.prototype.addVariable = function (identifier, blockScoped) { + if (blockScoped === void 0) { blockScoped = true; } + // block scoped variables go to the block scope, function scoped variables to the containing function scope + var scope = blockScoped ? this : this.functionScope; + var list = scope.variables.get(identifier.text); + if (list === undefined) { + scope.variables.set(identifier.text, [identifier]); + } + else { + list.push(identifier); + } + }; + return Scope; +}()); var NoShadowedVariableWalker = (function (_super) { tslib_1.__extends(NoShadowedVariableWalker, _super); function NoShadowedVariableWalker() { return _super !== null && _super.apply(this, arguments) || this; } - NoShadowedVariableWalker.prototype.createScope = function () { - return new Set(); - }; - NoShadowedVariableWalker.prototype.createBlockScope = function () { - return new Set(); - }; - NoShadowedVariableWalker.prototype.visitBindingElement = function (node) { - var isSingleVariable = node.name.kind === ts.SyntaxKind.Identifier; - if (isSingleVariable) { - var name = node.name; - var variableDeclaration = Lint.getBindingElementVariableDeclaration(node); - var isBlockScopedVariable = variableDeclaration !== null && Lint.isBlockScopedVariable(variableDeclaration); - this.handleSingleVariableIdentifier(name, isBlockScopedVariable); - } - _super.prototype.visitBindingElement.call(this, node); - }; - NoShadowedVariableWalker.prototype.visitCatchClause = function (node) { - // don't visit the catch clause variable declaration, just visit the block - // the catch clause variable declaration has its own special scoping rules - this.visitBlock(node.block); - }; - NoShadowedVariableWalker.prototype.visitCallSignature = function (_node) { - // don't call super, we don't need to check parameter names in call signatures - }; - NoShadowedVariableWalker.prototype.visitFunctionType = function (_node) { - // don't call super, we don't need to check names in function types - }; - NoShadowedVariableWalker.prototype.visitConstructorType = function (_node) { - // don't call super, we don't need to check names in constructor types - }; - NoShadowedVariableWalker.prototype.visitIndexSignatureDeclaration = function (_node) { - // don't call super, we don't want to walk index signatures - }; - NoShadowedVariableWalker.prototype.visitMethodSignature = function (_node) { - // don't call super, we don't want to walk method signatures either - }; - NoShadowedVariableWalker.prototype.visitParameterDeclaration = function (node) { - var isSingleParameter = node.name.kind === ts.SyntaxKind.Identifier; - if (isSingleParameter) { - this.handleSingleVariableIdentifier(node.name, false); - } - _super.prototype.visitParameterDeclaration.call(this, node); + NoShadowedVariableWalker.prototype.walk = function (sourceFile) { + var _this = this; + this.scope = new Scope(); + var cb = function (node) { + var parentScope = _this.scope; + if ((_this.options.function && tsutils_1.isFunctionExpression(node) || _this.options.class && tsutils_1.isClassExpression(node)) && + node.name !== undefined) { + /* special handling for named function and class expressions: + technically the name of the function is only visible inside of it, + but variables with the same name declared inside don't cause compiler errors. + Therefore we add an additional function scope only for the function name to avoid merging with other declarations */ + var functionScope = new Scope(); + functionScope.addVariable(node.name, false); + _this.scope = new Scope(); + if (tsutils_1.isClassExpression(node)) { + _this.visitClassLikeDeclaration(node, functionScope, cb); + } + else { + ts.forEachChild(node, cb); + } + _this.onScopeEnd(functionScope); + _this.scope = functionScope; + _this.onScopeEnd(parentScope); + _this.scope = parentScope; + return; + } + /* Visit decorators before entering a function scope. + In the AST decorators are children of the declaration they decorate, but we don't want to warn for the following code: + @decorator((param) => param) + function foo(param) {} + */ + if (node.decorators !== undefined) { + for (var _i = 0, _a = node.decorators; _i < _a.length; _i++) { + var decorator = _a[_i]; + ts.forEachChild(decorator, cb); + } + } + var boundary = tsutils_1.isScopeBoundary(node); + if (boundary === 2 /* Block */) { + _this.scope = new Scope(parentScope.functionScope); + } + else if (boundary === 1 /* Function */) { + _this.scope = new Scope(); + } + switch (node.kind) { + case ts.SyntaxKind.Decorator: + return; // handled above + case ts.SyntaxKind.VariableDeclarationList: + _this.handleVariableDeclarationList(node); + break; + case ts.SyntaxKind.TypeParameter: + if (_this.options.typeParameter) { + _this.scope.addVariable(node.name); + } + break; + case ts.SyntaxKind.FunctionDeclaration: + if (_this.options.function && node.name !== undefined) { + parentScope.addVariable(node.name, false); + } + break; + case ts.SyntaxKind.ClassDeclaration: + if (_this.options.class && node.name !== undefined) { + parentScope.addVariable(node.name); + } + // falls through + case ts.SyntaxKind.ClassExpression: + _this.visitClassLikeDeclaration(node, parentScope, cb); + _this.onScopeEnd(parentScope); + _this.scope = parentScope; + return; + case ts.SyntaxKind.TypeAliasDeclaration: + if (_this.options.typeAlias) { + parentScope.addVariable(node.name); + } + break; + case ts.SyntaxKind.EnumDeclaration: + if (_this.options.enum) { + parentScope.addVariable(node.name); + } + break; + case ts.SyntaxKind.InterfaceDeclaration: + if (_this.options.interface) { + parentScope.addVariable(node.name); + } + break; + case ts.SyntaxKind.Parameter: + if (node.parent.kind !== ts.SyntaxKind.IndexSignature && + !tsutils_1.isThisParameter(node) && + tsutils_1.isFunctionWithBody(node.parent)) { + _this.handleBindingName(node.name, false); + } + break; + case ts.SyntaxKind.ModuleDeclaration: + if (_this.options.namespace && + node.parent.kind !== ts.SyntaxKind.ModuleDeclaration && + node.name.kind === ts.SyntaxKind.Identifier) { + parentScope.addVariable(node.name, false); + } + break; + case ts.SyntaxKind.ImportClause: + if (_this.options.import && node.name !== undefined) { + _this.scope.addVariable(node.name, false); + } + break; + case ts.SyntaxKind.NamespaceImport: + case ts.SyntaxKind.ImportSpecifier: + case ts.SyntaxKind.ImportEqualsDeclaration: + if (_this.options.import) { + _this.scope.addVariable(node.name, false); + } + } + if (boundary !== 0 /* None */) { + ts.forEachChild(node, cb); + _this.onScopeEnd(parentScope); + _this.scope = parentScope; + } + else { + return ts.forEachChild(node, cb); + } + }; + ts.forEachChild(sourceFile, cb); + this.onScopeEnd(); }; - NoShadowedVariableWalker.prototype.visitTypeLiteral = function (_node) { - // don't call super, we don't want to walk the inside of type nodes + NoShadowedVariableWalker.prototype.visitClassLikeDeclaration = function (declaration, parentScope, cb) { + var _this = this; + var currentScope = this.scope; + ts.forEachChild(declaration, function (node) { + if (!tsutils_1.hasModifier(node.modifiers, ts.SyntaxKind.StaticKeyword)) { + return cb(node); + } + /* Don't treat static members as children of the class' scope. That avoid shadowed type parameter warnings on static members. + class C<T> { + static method<T>() {} + } + */ + _this.scope = parentScope; + cb(node); + _this.scope = currentScope; + }); }; - NoShadowedVariableWalker.prototype.visitVariableDeclaration = function (node) { - var isSingleVariable = node.name.kind === ts.SyntaxKind.Identifier; - if (isSingleVariable) { - this.handleSingleVariableIdentifier(node.name, Lint.isBlockScopedVariable(node)); + NoShadowedVariableWalker.prototype.handleVariableDeclarationList = function (node) { + var blockScoped = tsutils_1.isBlockScopedVariableDeclarationList(node); + for (var _i = 0, _a = node.declarations; _i < _a.length; _i++) { + var variable = _a[_i]; + this.handleBindingName(variable.name, blockScoped); } - _super.prototype.visitVariableDeclaration.call(this, node); }; - NoShadowedVariableWalker.prototype.handleSingleVariableIdentifier = function (variableIdentifier, isBlockScoped) { - var variableName = variableIdentifier.text; - if (this.isVarInCurrentScope(variableName) && !this.inCurrentBlockScope(variableName)) { - // shadowing if there's already a `var` of the same name in the scope AND - // it's not in the current block (handled by the 'no-duplicate-variable' rule) - this.addFailureOnIdentifier(variableIdentifier); - } - else if (this.inPreviousBlockScope(variableName)) { - // shadowing if there is a `var`, `let`, 'const`, or parameter in a previous block scope - this.addFailureOnIdentifier(variableIdentifier); + NoShadowedVariableWalker.prototype.handleBindingName = function (node, blockScoped) { + if (node.kind === ts.SyntaxKind.Identifier) { + this.scope.addVariable(node, blockScoped); } - if (!isBlockScoped) { - // `var` variables go on the scope - this.getCurrentScope().add(variableName); + else { + for (var _i = 0, _a = node.elements; _i < _a.length; _i++) { + var element = _a[_i]; + if (element.kind !== ts.SyntaxKind.OmittedExpression) { + this.handleBindingName(element.name, blockScoped); + } + } } - // all variables go on block scope, including `var` - this.getCurrentBlockScope().add(variableName); }; - NoShadowedVariableWalker.prototype.isVarInCurrentScope = function (varName) { - return this.getCurrentScope().has(varName); - }; - NoShadowedVariableWalker.prototype.inCurrentBlockScope = function (varName) { - return this.getCurrentBlockScope().has(varName); - }; - NoShadowedVariableWalker.prototype.inPreviousBlockScope = function (varName) { + NoShadowedVariableWalker.prototype.onScopeEnd = function (parent) { var _this = this; - return this.getAllBlockScopes().some(function (scopeInfo) { - return scopeInfo !== _this.getCurrentBlockScope() && scopeInfo.has(varName); + var _a = this.scope, variables = _a.variables, variablesSeen = _a.variablesSeen; + variablesSeen.forEach(function (identifiers, name) { + if (variables.has(name)) { + for (var _i = 0, identifiers_1 = identifiers; _i < identifiers_1.length; _i++) { + var identifier = identifiers_1[_i]; + _this.addFailureAtNode(identifier, Rule.FAILURE_STRING_FACTORY(name)); + } + } + else if (parent !== undefined) { + addToList(parent.variablesSeen, name, identifiers); + } }); - }; - NoShadowedVariableWalker.prototype.addFailureOnIdentifier = function (ident) { - var failureString = Rule.FAILURE_STRING_FACTORY(ident.text); - this.addFailureAtNode(ident, failureString); + if (parent !== undefined) { + variables.forEach(function (identifiers, name) { + addToList(parent.variablesSeen, name, identifiers); + }); + } }; return NoShadowedVariableWalker; -}(Lint.BlockScopeAwareRuleWalker)); +}(Lint.AbstractWalker)); +function addToList(map, name, identifiers) { + var list = map.get(name); + if (list === undefined) { + map.set(name, identifiers); + } + else { + list.push.apply(list, identifiers); + } +} +var _a; |