aboutsummaryrefslogtreecommitdiff
path: root/node_modules/tslint/lib/rules/preferConstRule.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/tslint/lib/rules/preferConstRule.js')
-rw-r--r--node_modules/tslint/lib/rules/preferConstRule.js310
1 files changed, 310 insertions, 0 deletions
diff --git a/node_modules/tslint/lib/rules/preferConstRule.js b/node_modules/tslint/lib/rules/preferConstRule.js
new file mode 100644
index 000000000..674a44755
--- /dev/null
+++ b/node_modules/tslint/lib/rules/preferConstRule.js
@@ -0,0 +1,310 @@
+"use strict";
+/**
+ * @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.
+ */
+Object.defineProperty(exports, "__esModule", { value: true });
+var tslib_1 = require("tslib");
+var ts = require("typescript");
+var Lint = require("../index");
+var utils = require("tsutils");
+var OPTION_DESTRUCTURING_ALL = "all";
+var OPTION_DESTRUCTURING_ANY = "any";
+var Rule = (function (_super) {
+ tslib_1.__extends(Rule, _super);
+ function Rule() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ /* tslint:enable:object-literal-sort-keys */
+ Rule.FAILURE_STRING_FACTORY = function (identifier, blockScoped) {
+ return "Identifier '" + identifier + "' is never reassigned; use 'const' instead of '" + (blockScoped ? "let" : "var") + "'.";
+ };
+ Rule.prototype.apply = function (sourceFile) {
+ var options = {
+ destructuringAll: this.ruleArguments.length !== 0 &&
+ this.ruleArguments[0].destructuring === OPTION_DESTRUCTURING_ALL,
+ };
+ var preferConstWalker = new PreferConstWalker(sourceFile, this.ruleName, options);
+ return this.applyWithWalker(preferConstWalker);
+ };
+ return Rule;
+}(Lint.Rules.AbstractRule));
+/* tslint:disable:object-literal-sort-keys */
+Rule.metadata = {
+ ruleName: "prefer-const",
+ description: "Requires that variable declarations use `const` instead of `let` and `var` if possible.",
+ descriptionDetails: (_a = ["\n If a variable is only assigned to once when it is declared, it should be declared using 'const'"], _a.raw = ["\n If a variable is only assigned to once when it is declared, it should be declared using 'const'"], Lint.Utils.dedent(_a)),
+ hasFix: true,
+ optionsDescription: (_b = ["\n An optional object containing the property \"destructuring\" with two possible values:\n\n * \"", "\" (default) - If any variable in destructuring can be const, this rule warns for those variables.\n * \"", "\" - Only warns if all variables in destructuring can be const."], _b.raw = ["\n An optional object containing the property \"destructuring\" with two possible values:\n\n * \"", "\" (default) - If any variable in destructuring can be const, this rule warns for those variables.\n * \"", "\" - Only warns if all variables in destructuring can be const."], Lint.Utils.dedent(_b, OPTION_DESTRUCTURING_ANY, OPTION_DESTRUCTURING_ALL)),
+ options: {
+ type: "object",
+ properties: {
+ destructuring: {
+ type: "string",
+ enum: [OPTION_DESTRUCTURING_ALL, OPTION_DESTRUCTURING_ANY],
+ },
+ },
+ },
+ optionExamples: [
+ true,
+ [true, { destructuring: OPTION_DESTRUCTURING_ALL }],
+ ],
+ type: "maintainability",
+ typescriptOnly: false,
+};
+exports.Rule = Rule;
+var Scope = (function () {
+ function Scope(functionScope) {
+ this.variables = 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 ? this : functionScope;
+ }
+ Scope.prototype.addVariable = function (identifier, declarationInfo, destructuringInfo) {
+ // block scoped variables go to the block scope, function scoped variables to the containing function scope
+ var scope = declarationInfo.isBlockScoped ? this : this.functionScope;
+ scope.variables.set(identifier.text, {
+ declarationInfo: declarationInfo,
+ destructuringInfo: destructuringInfo,
+ identifier: identifier,
+ reassigned: false,
+ });
+ };
+ return Scope;
+}());
+var PreferConstWalker = (function (_super) {
+ tslib_1.__extends(PreferConstWalker, _super);
+ function PreferConstWalker() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ PreferConstWalker.prototype.walk = function (sourceFile) {
+ var _this = this;
+ // don't check anything on declaration files
+ if (sourceFile.isDeclarationFile) {
+ return;
+ }
+ this.scope = new Scope();
+ var cb = function (node) {
+ var savedScope = _this.scope;
+ var boundary = utils.isScopeBoundary(node);
+ if (boundary !== 0 /* None */) {
+ if (boundary === 1 /* Function */) {
+ if (node.kind === ts.SyntaxKind.ModuleDeclaration && utils.hasModifier(node.modifiers, ts.SyntaxKind.DeclareKeyword)) {
+ // don't check ambient namespaces
+ return;
+ }
+ _this.scope = new Scope();
+ if (utils.isFunctionDeclaration(node) ||
+ utils.isMethodDeclaration(node) ||
+ utils.isFunctionExpression(node) ||
+ utils.isArrowFunction(node) ||
+ utils.isConstructorDeclaration(node)) {
+ // special handling for function parameters
+ // each parameter initializer can only reassign preceding parameters of variables of the containing scope
+ if (node.body !== undefined) {
+ for (var _i = 0, _a = node.parameters; _i < _a.length; _i++) {
+ var param = _a[_i];
+ cb(param);
+ _this.settle(savedScope);
+ }
+ cb(node.body);
+ _this.onScopeEnd(savedScope);
+ }
+ _this.scope = savedScope;
+ return;
+ }
+ }
+ else {
+ _this.scope = new Scope(_this.scope.functionScope);
+ if ((utils.isForInStatement(node) || utils.isForOfStatement(node)) &&
+ node.initializer.kind !== ts.SyntaxKind.VariableDeclarationList) {
+ _this.handleExpression(node.initializer);
+ }
+ }
+ }
+ if (node.kind === ts.SyntaxKind.VariableDeclarationList) {
+ _this.handleVariableDeclaration(node);
+ }
+ else if (node.kind === ts.SyntaxKind.CatchClause) {
+ _this.handleBindingName(node.variableDeclaration.name, {
+ canBeConst: false,
+ isBlockScoped: true,
+ });
+ }
+ else if (node.kind === ts.SyntaxKind.Parameter) {
+ _this.handleBindingName(node.name, {
+ canBeConst: false,
+ isBlockScoped: true,
+ });
+ }
+ else if (utils.isPostfixUnaryExpression(node) ||
+ utils.isPrefixUnaryExpression(node) &&
+ (node.operator === ts.SyntaxKind.PlusPlusToken || node.operator === ts.SyntaxKind.MinusMinusToken)) {
+ if (utils.isIdentifier(node.operand)) {
+ _this.scope.reassigned.add(node.operand.text);
+ }
+ }
+ else if (utils.isBinaryExpression(node) && utils.isAssignmentKind(node.operatorToken.kind)) {
+ _this.handleExpression(node.left);
+ }
+ if (boundary !== 0 /* None */) {
+ ts.forEachChild(node, cb);
+ _this.onScopeEnd(savedScope);
+ _this.scope = savedScope;
+ }
+ else {
+ return ts.forEachChild(node, cb);
+ }
+ };
+ if (ts.isExternalModule(sourceFile)) {
+ ts.forEachChild(sourceFile, cb);
+ this.onScopeEnd();
+ }
+ else {
+ return ts.forEachChild(sourceFile, cb);
+ }
+ };
+ PreferConstWalker.prototype.handleExpression = function (node) {
+ switch (node.kind) {
+ case ts.SyntaxKind.Identifier:
+ this.scope.reassigned.add(node.text);
+ break;
+ case ts.SyntaxKind.ParenthesizedExpression:
+ this.handleExpression(node.expression);
+ break;
+ case ts.SyntaxKind.ArrayLiteralExpression:
+ for (var _i = 0, _a = node.elements; _i < _a.length; _i++) {
+ var element = _a[_i];
+ if (element.kind === ts.SyntaxKind.SpreadElement) {
+ this.handleExpression(element.expression);
+ }
+ else {
+ this.handleExpression(element);
+ }
+ }
+ break;
+ case ts.SyntaxKind.ObjectLiteralExpression:
+ for (var _b = 0, _c = node.properties; _b < _c.length; _b++) {
+ var property = _c[_b];
+ switch (property.kind) {
+ case ts.SyntaxKind.ShorthandPropertyAssignment:
+ this.scope.reassigned.add(property.name.text);
+ break;
+ case ts.SyntaxKind.SpreadAssignment:
+ if (property.name !== undefined) {
+ this.scope.reassigned.add(property.name.text);
+ }
+ else {
+ // handle `...(variable)`
+ this.handleExpression(property.expression);
+ }
+ break;
+ default:
+ this.handleExpression(property.initializer);
+ }
+ }
+ break;
+ }
+ };
+ PreferConstWalker.prototype.handleBindingName = function (name, declarationInfo) {
+ var _this = this;
+ if (name.kind === ts.SyntaxKind.Identifier) {
+ this.scope.addVariable(name, declarationInfo);
+ }
+ else {
+ var destructuringInfo_1 = {
+ reassignedSiblings: false,
+ };
+ utils.forEachDestructuringIdentifier(name, function (declaration) { return _this.scope.addVariable(declaration.name, declarationInfo, destructuringInfo_1); });
+ }
+ };
+ PreferConstWalker.prototype.handleVariableDeclaration = function (declarationList) {
+ var declarationInfo;
+ var kind = utils.getVariableDeclarationKind(declarationList);
+ if (kind === 2 /* Const */ ||
+ utils.hasModifier(declarationList.parent.modifiers, ts.SyntaxKind.ExportKeyword, ts.SyntaxKind.DeclareKeyword)) {
+ declarationInfo = {
+ canBeConst: false,
+ isBlockScoped: kind !== 0 /* Var */,
+ };
+ }
+ else {
+ declarationInfo = {
+ allInitialized: declarationList.parent.kind === ts.SyntaxKind.ForOfStatement ||
+ declarationList.parent.kind === ts.SyntaxKind.ForInStatement ||
+ declarationList.declarations.every(function (declaration) { return declaration.initializer !== undefined; }),
+ canBeConst: true,
+ declarationList: declarationList,
+ isBlockScoped: kind === 1 /* Let */,
+ isForLoop: declarationList.parent.kind === ts.SyntaxKind.ForStatement,
+ reassignedSiblings: false,
+ };
+ }
+ for (var _i = 0, _a = declarationList.declarations; _i < _a.length; _i++) {
+ var declaration = _a[_i];
+ this.handleBindingName(declaration.name, declarationInfo);
+ }
+ };
+ PreferConstWalker.prototype.settle = function (parent) {
+ var _a = this.scope, variables = _a.variables, reassigned = _a.reassigned;
+ reassigned.forEach(function (name) {
+ var variableInfo = variables.get(name);
+ if (variableInfo !== undefined) {
+ if (variableInfo.declarationInfo.canBeConst) {
+ variableInfo.reassigned = true;
+ variableInfo.declarationInfo.reassignedSiblings = true;
+ if (variableInfo.destructuringInfo !== undefined) {
+ variableInfo.destructuringInfo.reassignedSiblings = true;
+ }
+ }
+ }
+ else if (parent !== undefined) {
+ // if the reassigned variable was not declared in this scope we defer to the parent scope
+ parent.reassigned.add(name);
+ }
+ });
+ reassigned.clear();
+ };
+ PreferConstWalker.prototype.onScopeEnd = function (parent) {
+ var _this = this;
+ this.settle(parent);
+ var appliedFixes = new Set();
+ this.scope.variables.forEach(function (info, name) {
+ if (info.declarationInfo.canBeConst &&
+ !info.reassigned &&
+ // don't add failures for reassigned variables in for loop initializer
+ !(info.declarationInfo.reassignedSiblings && info.declarationInfo.isForLoop) &&
+ // if {destructuring: "all"} is set, only add a failure if all variables in a destructuring assignment can be const
+ (!_this.options.destructuringAll ||
+ info.destructuringInfo === undefined ||
+ !info.destructuringInfo.reassignedSiblings)) {
+ var fix = void 0;
+ // only apply fixes if the VariableDeclarationList has no reassigned variables
+ // and the variable is block scoped aka `let` and initialized
+ if (info.declarationInfo.allInitialized &&
+ !info.declarationInfo.reassignedSiblings &&
+ info.declarationInfo.isBlockScoped &&
+ !appliedFixes.has(info.declarationInfo.declarationList)) {
+ fix = new Lint.Replacement(info.declarationInfo.declarationList.getStart(_this.sourceFile), 3, "const");
+ // add only one fixer per VariableDeclarationList
+ appliedFixes.add(info.declarationInfo.declarationList);
+ }
+ _this.addFailureAtNode(info.identifier, Rule.FAILURE_STRING_FACTORY(name, info.declarationInfo.isBlockScoped), fix);
+ }
+ });
+ };
+ return PreferConstWalker;
+}(Lint.AbstractWalker));
+var _a, _b;