From 4fb35f97799a34e268941600385a75dfbeba54ce Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 15 Mar 2019 10:17:35 +0900 Subject: [PATCH 01/13] Breaking: make no-redeclare checking comments - Implement https://github.com/eslint/rfcs/pull/17 - Fixes #11370 - Remvoes the access to parserOptions from no-redeclare rule - Adds several tests for lexical bindings to no-redeclare rule --- lib/config/config-ops.js | 6 +- lib/linter.js | 63 ++++--- lib/rules/no-redeclare.js | 148 ++++++++++++----- lib/rules/no-unused-vars.js | 55 ++---- lib/util/ast-utils.js | 25 +++ package.json | 1 + tests/lib/config/config-initializer.js | 2 +- tests/lib/config/config-ops.js | 22 +-- tests/lib/linter.js | 12 +- tests/lib/rules/no-redeclare.js | 222 ++++++++++++++++++++++++- tests/tools/loose-parser.js | 31 ++++ 11 files changed, 448 insertions(+), 139 deletions(-) create mode 100644 tests/tools/loose-parser.js diff --git a/lib/config/config-ops.js b/lib/config/config-ops.js index b38cdf7d7ca..d9714f997c3 100644 --- a/lib/config/config-ops.js +++ b/lib/config/config-ops.js @@ -387,18 +387,18 @@ module.exports = { case "true": case "writeable": case "writable": - return "writeable"; + return "writable"; case null: case false: case "false": case "readable": case "readonly": - return "readable"; + return "readonly"; // Fallback to minimize compatibility impact default: - return "writeable"; + return "writable"; } } }; diff --git a/lib/linter.js b/lib/linter.js index 88448d90f8a..bf2cc7cdf81 100644 --- a/lib/linter.js +++ b/lib/linter.js @@ -70,34 +70,36 @@ const commentParser = new ConfigCommentParser(); * @param {{exportedVariables: Object, enabledGlobals: Object}} commentDirectives Directives from comment configuration * @returns {void} */ -function addDeclaredGlobals(globalScope, configGlobals, commentDirectives) { - const mergedGlobalsInfo = Object.assign( - {}, - lodash.mapValues(configGlobals, value => ({ sourceComment: null, value: ConfigOps.normalizeConfigGlobal(value) })), - lodash.mapValues(commentDirectives.enabledGlobals, ({ comment, value }) => ({ sourceComment: comment, value: ConfigOps.normalizeConfigGlobal(value) })) - ); +function addDeclaredGlobals(globalScope, configGlobals, { exportedVariables, enabledGlobals }) { - Object.keys(mergedGlobalsInfo) - .filter(name => mergedGlobalsInfo[name].value !== "off") - .forEach(name => { - let variable = globalScope.set.get(name); - - if (!variable) { - variable = new eslintScope.Variable(name, globalScope); - if (mergedGlobalsInfo[name].sourceComment === null) { - variable.eslintExplicitGlobal = false; - } else { - variable.eslintExplicitGlobal = true; - variable.eslintExplicitGlobalComment = mergedGlobalsInfo[name].sourceComment; - } - globalScope.variables.push(variable); - globalScope.set.set(name, variable); - } - variable.writeable = (mergedGlobalsInfo[name].value === "writeable"); - }); + // Define configured global variables. + for (const id of new Set([...Object.keys(configGlobals), ...Object.keys(enabledGlobals)])) { + const configValue = configGlobals[id] === void 0 ? void 0 : ConfigOps.normalizeConfigGlobal(configGlobals[id]); + const commentValue = enabledGlobals[id] && ConfigOps.normalizeConfigGlobal(enabledGlobals[id].value); + const value = commentValue || configValue; + const sourceComments = enabledGlobals[id] && enabledGlobals[id].comments; + + if (value === "off") { + continue; + } + + let variable = globalScope.set.get(id); + + if (!variable) { + variable = new eslintScope.Variable(id, globalScope); + + globalScope.variables.push(variable); + globalScope.set.set(id, variable); + } + + variable.eslintImplicitGlobalSetting = configValue; + variable.eslintExplicitGlobal = sourceComments !== void 0; + variable.eslintExplicitGlobalComments = sourceComments; + variable.writeable = (value === "writable"); + } // mark all exported variables as such - Object.keys(commentDirectives.exportedVariables).forEach(name => { + Object.keys(exportedVariables).forEach(name => { const variable = globalScope.set.get(name); if (variable) { @@ -152,7 +154,7 @@ function createDisableDirectives(type, loc, value) { * @param {string} filename The file being checked. * @param {ASTNode} ast The top node of the AST. * @param {function(string): {create: Function}} ruleMapper A map from rule IDs to defined rules - * @returns {{configuredRules: Object, enabledGlobals: Object, exportedVariables: Object, problems: Problem[], disableDirectives: DisableDirective[]}} + * @returns {{configuredRules: Object, enabledGlobals: {value:string,comment:Token}[], exportedVariables: Object, problems: Problem[], disableDirectives: DisableDirective[]}} * A collection of the directive comments that were found, along with any problems that occurred when parsing */ function getDirectiveComments(filename, ast, ruleMapper) { @@ -197,7 +199,14 @@ function getDirectiveComments(filename, ast, ruleMapper) { case "globals": case "global": - Object.assign(enabledGlobals, commentParser.parseStringConfig(directiveValue, comment)); + for (const [id, { value }] of Object.entries(commentParser.parseStringConfig(directiveValue, comment))) { + if (enabledGlobals[id]) { + enabledGlobals[id].comments.push(comment); + enabledGlobals[id].value = value; + } else { + enabledGlobals[id] = { comments: [comment], value }; + } + } break; case "eslint-disable": diff --git a/lib/rules/no-redeclare.js b/lib/rules/no-redeclare.js index 4d689cc6138..cee85187500 100644 --- a/lib/rules/no-redeclare.js +++ b/lib/rules/no-redeclare.js @@ -5,6 +5,12 @@ "use strict"; +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const astUtils = require("../util/ast-utils"); + //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -20,6 +26,12 @@ module.exports = { url: "https://eslint.org/docs/rules/no-redeclare" }, + messages: { + redeclared: "'{{id}}' is already defined.", + redeclaredAsBuiltin: "'{{id}}' is already defined as a built-in global variable.", + redeclaredBySyntax: "'{{id}}' is already defined by a variable declaration." + }, + schema: [ { type: "object", @@ -33,72 +45,128 @@ module.exports = { create(context) { const options = { - builtinGlobals: context.options[0] && context.options[0].builtinGlobals + builtinGlobals: Boolean( + context.options[0] && + context.options[0].builtinGlobals + ) }; + const sourceCode = context.getSourceCode(); /** - * Find variables in a given scope and flag redeclared ones. - * @param {Scope} scope - An eslint-scope scope object. - * @returns {void} - * @private + * Iterate declarations of a given variable. + * @param {escope.variable} variable The variable object to iterate declarations. + * @returns {IterableIterator<{type:string,node:ASTNode,loc:SourceLocation}>} The declarations. */ - function findVariablesInScope(scope) { - scope.variables.forEach(variable => { - const hasBuiltin = options.builtinGlobals && "writeable" in variable; - const count = (hasBuiltin ? 1 : 0) + variable.identifiers.length; + function *iterateDeclarations(variable) { + if (options.builtinGlobals && ( + variable.eslintImplicitGlobalSetting === "readonly" || + variable.eslintImplicitGlobalSetting === "writable" + )) { + yield { type: "builtin" }; + } - if (count >= 2) { - variable.identifiers.sort((a, b) => a.range[1] - b.range[1]); + for (const id of variable.identifiers) { + yield { type: "syntax", node: id, loc: id.loc }; + } - for (let i = (hasBuiltin ? 0 : 1), l = variable.identifiers.length; i < l; i++) { - context.report({ node: variable.identifiers[i], message: "'{{a}}' is already defined.", data: { a: variable.name } }); - } + if (variable.eslintExplicitGlobalComments) { + for (const comment of variable.eslintExplicitGlobalComments) { + yield { + type: "comment", + node: comment, + loc: astUtils.getNameLocationInGlobalDirectiveComment( + sourceCode, + comment, + variable.name + ) + }; } - }); - + } } /** - * Find variables in the current scope. - * @param {ASTNode} node - The Program node. + * Find variables in a given scope and flag redeclared ones. + * @param {Scope} scope - An eslint-scope scope object. * @returns {void} * @private */ - function checkForGlobal(node) { - const scope = context.getScope(), - parserOptions = context.parserOptions, - ecmaFeatures = parserOptions.ecmaFeatures || {}; - - // Nodejs env or modules has a special scope. - if (ecmaFeatures.globalReturn || node.sourceType === "module") { - findVariablesInScope(scope.childScopes[0]); - } else { - findVariablesInScope(scope); + function findVariablesInScope(scope) { + for (const variable of scope.variables) { + const [ + declaration, + ...extraDeclarations + ] = iterateDeclarations(variable); + + if (extraDeclarations.length === 0) { + continue; + } + + /* + * If the type of a declaration is different from the type of + * the first declaration, it shows the location of the first + * declaration. + */ + const detailMessageId = declaration.type === "builtin" + ? "redeclaredAsBuiltin" + : "redeclaredBySyntax"; + const data = { id: variable.name }; + + // Report extra declarations. + for (const { type, node, loc } of extraDeclarations) { + const messageId = type === declaration.type + ? "redeclared" + : detailMessageId; + + context.report({ node, loc, messageId, data }); + } } } /** * Find variables in the current scope. + * @param {ASTNode} node The node of the current scope. * @returns {void} * @private */ - function checkForBlock() { - findVariablesInScope(context.getScope()); - } + function checkForBlock(node) { + const scope = context.getScope(); - if (context.parserOptions.ecmaVersion >= 6) { - return { - Program: checkForGlobal, - BlockStatement: checkForBlock, - SwitchStatement: checkForBlock - }; + /* + * In ES5, some node type such as `BlockStatement` doesn't have that scope. + * `scope.block` is a different node in such a case. + */ + if (scope.block === node) { + findVariablesInScope(scope); + } } + return { - Program: checkForGlobal, + Program() { + const scope = context.getScope(); + + findVariablesInScope(scope); + + // Node.js or ES modules has a special scope. + if ( + scope.type === "global" && + scope.childScopes[0] && + + // The special scope's block is the Program node. + scope.block === scope.childScopes[0].block + ) { + findVariablesInScope(scope.childScopes[0]); + } + }, + FunctionDeclaration: checkForBlock, FunctionExpression: checkForBlock, - ArrowFunctionExpression: checkForBlock - }; + ArrowFunctionExpression: checkForBlock, + BlockStatement: checkForBlock, + ForStatement: checkForBlock, + ForInStatement: checkForBlock, + ForOfStatement: checkForBlock, + SwitchStatement: checkForBlock + }; } }; diff --git a/lib/rules/no-unused-vars.js b/lib/rules/no-unused-vars.js index d4c4c6355c2..ce8f6cd8fbf 100644 --- a/lib/rules/no-unused-vars.js +++ b/lib/rules/no-unused-vars.js @@ -9,7 +9,6 @@ // Requirements //------------------------------------------------------------------------------ -const lodash = require("lodash"); const astUtils = require("../util/ast-utils"); //------------------------------------------------------------------------------ @@ -588,39 +587,6 @@ module.exports = { return unusedVars; } - /** - * Gets the index of a given variable name in a given comment. - * @param {eslint-scope.Variable} variable - A variable to get. - * @param {ASTNode} comment - A comment node which includes the variable name. - * @returns {number} The index of the variable name's location. - * @private - */ - function getColumnInComment(variable, comment) { - const namePattern = new RegExp(`[\\s,]${lodash.escapeRegExp(variable.name)}(?:$|[\\s,:])`, "gu"); - - // To ignore the first text "global". - namePattern.lastIndex = comment.value.indexOf("global") + 6; - - // Search a given variable name. - const match = namePattern.exec(comment.value); - - return match ? match.index + 1 : 0; - } - - /** - * Creates the correct location of a given variables. - * The location is at its name string in a `/*global` comment. - * - * @param {eslint-scope.Variable} variable - A variable to get its location. - * @returns {{line: number, column: number}} The location object for the variable. - * @private - */ - function getLocation(variable) { - const comment = variable.eslintExplicitGlobalComment; - - return sourceCode.getLocFromIndex(comment.range[0] + 2 + getColumnInComment(variable, comment)); - } - //-------------------------------------------------------------------------- // Public //-------------------------------------------------------------------------- @@ -632,14 +598,8 @@ module.exports = { for (let i = 0, l = unusedVars.length; i < l; ++i) { const unusedVar = unusedVars[i]; - if (unusedVar.eslintExplicitGlobal) { - context.report({ - node: programNode, - loc: getLocation(unusedVar), - message: getDefinedMessage(unusedVar), - data: unusedVar - }); - } else if (unusedVar.defs.length > 0) { + // Report the first declaration. + if (unusedVar.defs.length > 0) { context.report({ node: unusedVar.identifiers[0], message: unusedVar.references.some(ref => ref.isWrite()) @@ -647,6 +607,17 @@ module.exports = { : getDefinedMessage(unusedVar), data: unusedVar }); + + // If there are no regular declaration, report the first `/*globals*/` comment directive. + } else if (unusedVar.eslintExplicitGlobalComments && unusedVar.eslintExplicitGlobalComments.length > 0) { + const directiveComment = unusedVar.eslintExplicitGlobalComments[0]; + + context.report({ + node: programNode, + loc: astUtils.getNameLocationInGlobalDirectiveComment(sourceCode, directiveComment, unusedVar.name), + message: getDefinedMessage(unusedVar), + data: unusedVar + }); } } } diff --git a/lib/util/ast-utils.js b/lib/util/ast-utils.js index 85205a75d24..d00ee4f01c4 100644 --- a/lib/util/ast-utils.js +++ b/lib/util/ast-utils.js @@ -11,6 +11,7 @@ const esutils = require("esutils"); const espree = require("espree"); +const lodash = require("lodash"); //------------------------------------------------------------------------------ // Helpers @@ -1342,5 +1343,29 @@ module.exports = { } return false; + }, + + /** + * Get the `loc` object of a given name in a `/*globals` directive comment. + * @param {SourceCode} sourceCode The source code to convert index to loc. + * @param {Comment} comment The `/*globals` directive comment which include the name. + * @param {string} name The name to find. + * @returns {SourceLocation} The `loc` object. + */ + getNameLocationInGlobalDirectiveComment(sourceCode, comment, name) { + const namePattern = new RegExp(`[\\s,]${lodash.escapeRegExp(name)}(?:$|[\\s,:])`, "gu"); + + // To ignore the first text "global". + namePattern.lastIndex = comment.value.indexOf("global") + 6; + + // Search a given variable name. + const match = namePattern.exec(comment.value); + + // Convert the index to loc. + return sourceCode.getLocFromIndex( + comment.range[0] + + 2 + // 2 is "/*".length + (match ? match.index + 1 : 0) + ); } }; diff --git a/package.json b/package.json index 2d9f0d0be15..6f4fad5f9f4 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "@babel/core": "^7.2.2", "@babel/polyfill": "^7.2.5", "@babel/preset-env": "^7.3.1", + "acorn": "^6.1.1", "babel-loader": "^8.0.5", "beefy": "^2.1.8", "brfs": "^2.0.0", diff --git a/tests/lib/config/config-initializer.js b/tests/lib/config/config-initializer.js index d78651b1b45..8c5603bb584 100644 --- a/tests/lib/config/config-initializer.js +++ b/tests/lib/config/config-initializer.js @@ -76,7 +76,7 @@ describe("configInitializer", () => { // copy into clean area so as not to get "infected" by this project's .eslintrc files before(() => { - fixtureDir = `${os.tmpdir()}/eslint/fixtures/config-initializer`; + fixtureDir = path.join(os.tmpdir(), "eslint/fixtures/config-initializer"); sh.mkdir("-p", fixtureDir); sh.cp("-r", "./tests/fixtures/config-initializer/.", fixtureDir); fixtureDir = fs.realpathSync(fixtureDir); diff --git a/tests/lib/config/config-ops.js b/tests/lib/config/config-ops.js index 8d5ad5fa5c8..2b4935b9ffe 100644 --- a/tests/lib/config/config-ops.js +++ b/tests/lib/config/config-ops.js @@ -902,17 +902,17 @@ describe("ConfigOps", () => { describe("normalizeConfigGlobal", () => { [ ["off", "off"], - [true, "writeable"], - ["true", "writeable"], - [false, "readable"], - ["false", "readable"], - [null, "readable"], - ["writeable", "writeable"], - ["writable", "writeable"], - ["readable", "readable"], - ["readonly", "readable"], - ["writable", "writeable"], - ["something else", "writeable"] + [true, "writable"], + ["true", "writable"], + [false, "readonly"], + ["false", "readonly"], + [null, "readonly"], + ["writeable", "writable"], + ["writable", "writable"], + ["readable", "readonly"], + ["readonly", "readonly"], + ["writable", "writable"], + ["something else", "writable"] ].forEach(([input, output]) => { it(util.inspect(input), () => { assert.strictEqual(ConfigOps.normalizeConfigGlobal(input), output); diff --git a/tests/lib/linter.js b/tests/lib/linter.js index 3771ceb261c..d0110610b75 100644 --- a/tests/lib/linter.js +++ b/tests/lib/linter.js @@ -3467,18 +3467,18 @@ describe("Linter", () => { const foo = getVariable(scope, "foo"); - assert.strictEqual(true, foo.eslintExplicitGlobal); - assert.strictEqual(comments[0], foo.eslintExplicitGlobalComment); + assert.strictEqual(foo.eslintExplicitGlobal, true); + assert.strictEqual(foo.eslintExplicitGlobalComments[0], comments[0]); const bar = getVariable(scope, "bar"); - assert.strictEqual(true, bar.eslintExplicitGlobal); - assert.strictEqual(comments[1], bar.eslintExplicitGlobalComment); + assert.strictEqual(bar.eslintExplicitGlobal, true); + assert.strictEqual(bar.eslintExplicitGlobalComments[0], comments[1]); const baz = getVariable(scope, "baz"); - assert.strictEqual(true, baz.eslintExplicitGlobal); - assert.strictEqual(comments[1], baz.eslintExplicitGlobalComment); + assert.strictEqual(baz.eslintExplicitGlobal, true); + assert.strictEqual(baz.eslintExplicitGlobalComments[0], comments[1]); ok = true; } diff --git a/tests/lib/rules/no-redeclare.js b/tests/lib/rules/no-redeclare.js index 2cd0f8ef57d..63d1f692a41 100644 --- a/tests/lib/rules/no-redeclare.js +++ b/tests/lib/rules/no-redeclare.js @@ -9,13 +9,15 @@ // Requirements //------------------------------------------------------------------------------ -const rule = require("../../../lib/rules/no-redeclare"), - RuleTester = require("../../../lib/testers/rule-tester"); +const path = require("path"); +const rule = require("../../../lib/rules/no-redeclare"); +const RuleTester = require("../../../lib/testers/rule-tester"); //------------------------------------------------------------------------------ // Tests //------------------------------------------------------------------------------ +const looseParserPath = path.resolve(__dirname, "../../tools/loose-parser.js"); const ruleTester = new RuleTester(); ruleTester.run("no-redeclare", rule, { @@ -40,6 +42,32 @@ ruleTester.run("no-redeclare", rule, { code: "var self = 1", options: [{ builtinGlobals: true }], env: { browser: false } + }, + + // Comments and built-ins. + { + code: "/*globals Array */", + options: [{ builtinGlobals: false }] + }, + { + code: "/*globals a */", + options: [{ builtinGlobals: false }], + globals: { a: "readonly" } + }, + { + code: "/*globals a */", + options: [{ builtinGlobals: false }], + globals: { a: "writable" } + }, + { + code: "/*globals a:off */", + options: [{ builtinGlobals: true }], + globals: { a: "readonly" } + }, + { + code: "/*globals a */", + options: [{ builtinGlobals: true }], + globals: { a: "off" } } ], invalid: [ @@ -57,13 +85,13 @@ ruleTester.run("no-redeclare", rule, { { code: "var Object = 0;", options: [{ builtinGlobals: true }], - errors: [{ message: "'Object' is already defined.", type: "Identifier" }] + errors: [{ message: "'Object' is already defined as a built-in global variable.", type: "Identifier" }] }, { code: "var top = 0;", options: [{ builtinGlobals: true }], - errors: [{ message: "'top' is already defined.", type: "Identifier" }], - env: { browser: true } + env: { browser: true }, + errors: [{ message: "'top' is already defined as a built-in global variable.", type: "Identifier" }] }, { code: "var a; var {a = 0, b: Object = 0} = {};", @@ -71,7 +99,7 @@ ruleTester.run("no-redeclare", rule, { parserOptions: { ecmaVersion: 6 }, errors: [ { message: "'a' is already defined.", type: "Identifier" }, - { message: "'Object' is already defined.", type: "Identifier" } + { message: "'Object' is already defined as a built-in global variable.", type: "Identifier" } ] }, { @@ -98,13 +126,189 @@ ruleTester.run("no-redeclare", rule, { { message: "'a' is already defined.", type: "Identifier" } ] }, - - // Notifications of readonly are moved from no-undef: https://github.com/eslint/eslint/issues/4504 { code: "/*global b:false*/ var b = 1;", options: [{ builtinGlobals: true }], errors: [ - { message: "'b' is already defined.", type: "Identifier" } + { message: "'b' is already defined by a variable declaration.", type: "Block" } + ] + }, + { + code: "/*global b:true*/ var b = 1;", + options: [{ builtinGlobals: true }], + errors: [ + { message: "'b' is already defined by a variable declaration.", type: "Block" } + ] + }, + { + code: "function f() { var a; var a; }", + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "function f(a) { var a; }", + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "function f() { var a; if (test) { var a; } }", + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "for (var a, a;;);", + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + + // let/const + { + code: "let a; let a;", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "let a; let a;", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015, sourceType: "module" }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "let a; let a;", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015, ecmaFeatures: { globalReturn: true } }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "let a; const a = 0;", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "const a = 0; const a = 0;", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "if (test) { let a; let a; }", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "switch (test) { case 0: let a; let a; }", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "for (let a, a;;);", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "for (let [a, a] in xs);", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "for (let [a, a] of xs);", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "function f() { let a; let a; }", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "function f(a) { let a; }", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + { + code: "function f() { if (test) { let a; let a; } }", + parser: looseParserPath, + parserOptions: { ecmaVersion: 2015 }, + errors: [ + { message: "'a' is already defined.", type: "Identifier" } + ] + }, + + // Comments and built-ins. + { + code: "/*globals Array */", + options: [{ builtinGlobals: true }], + errors: [ + { message: "'Array' is already defined as a built-in global variable.", type: "Block" } + ] + }, + { + code: "/*globals a */", + options: [{ builtinGlobals: true }], + globals: { a: "readonly" }, + errors: [ + { message: "'a' is already defined as a built-in global variable.", type: "Block" } + ] + }, + { + code: "/*globals a */", + options: [{ builtinGlobals: true }], + globals: { a: "writable" }, + errors: [ + { message: "'a' is already defined as a built-in global variable.", type: "Block" } + ] + }, + { + code: "/*globals a */ /*globals a */", + errors: [ + { message: "'a' is already defined.", type: "Block" } + ] + }, + { + code: "/*globals a */ /*globals a */ var a = 0", + options: [{ builtinGlobals: true }], + globals: { a: "writable" }, + errors: [ + { message: "'a' is already defined as a built-in global variable.", type: "Block" }, + { message: "'a' is already defined as a built-in global variable.", type: "Block" }, + { message: "'a' is already defined as a built-in global variable.", type: "Identifier" } ] } ] diff --git a/tests/tools/loose-parser.js b/tests/tools/loose-parser.js new file mode 100644 index 00000000000..abb3aeb5bd0 --- /dev/null +++ b/tests/tools/loose-parser.js @@ -0,0 +1,31 @@ +/** + * @fileoverview Define a custom parser to ignore recoverable syntax errors. + * @author Toru Nagashima + * + * no-redeclare rule uses this parser to check redeclarations. + */ +"use strict"; + +const acorn = require("acorn"); +const espree = require("espree/lib/espree"); + +// eslint-disable-next-line valid-jsdoc +/** + * Define the parser which ignores recoverable errors. + * @returns {(parser:acorn.Parser) => acorn.Parser} The function that defines loose parser. + */ +function loose() { + return Parser => class LooseParser extends Parser { + raiseRecoverable() { // eslint-disable-line class-methods-use-this + // ignore + } + }; +} + +const LooseEspree = acorn.Parser.extend(espree(), loose()); + +module.exports = { + parse(code, options) { + return new LooseEspree(options, code).parse(); + } +}; From 722537a56cc143a6bf48f2521605111cc617f738 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 15 Mar 2019 10:28:02 +0900 Subject: [PATCH 02/13] Breaking: enable no-redeclare builtinGlobals - Fixes #11405 --- lib/rules/no-redeclare.js | 6 +++--- tests/lib/linter.js | 1 - tests/lib/rules/no-redeclare.js | 16 ++++++++++++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/rules/no-redeclare.js b/lib/rules/no-redeclare.js index cee85187500..180f0261350 100644 --- a/lib/rules/no-redeclare.js +++ b/lib/rules/no-redeclare.js @@ -36,7 +36,7 @@ module.exports = { { type: "object", properties: { - builtinGlobals: { type: "boolean", default: false } + builtinGlobals: { type: "boolean", default: true } }, additionalProperties: false } @@ -46,8 +46,8 @@ module.exports = { create(context) { const options = { builtinGlobals: Boolean( - context.options[0] && - context.options[0].builtinGlobals + context.options.length === 0 || + context.options[0].builtinGlobals !== false ) }; const sourceCode = context.getSourceCode(); diff --git a/tests/lib/linter.js b/tests/lib/linter.js index d0110610b75..63e5d271eb7 100644 --- a/tests/lib/linter.js +++ b/tests/lib/linter.js @@ -2,7 +2,6 @@ * @fileoverview Tests for eslint object. * @author Nicholas C. Zakas */ -/* globals window */ "use strict"; diff --git a/tests/lib/rules/no-redeclare.js b/tests/lib/rules/no-redeclare.js index 63d1f692a41..06579fb96f9 100644 --- a/tests/lib/rules/no-redeclare.js +++ b/tests/lib/rules/no-redeclare.js @@ -30,11 +30,9 @@ ruleTester.run("no-redeclare", rule, { ecmaVersion: 6 } }, - "var Object = 0;", { code: "var Object = 0;", options: [{ builtinGlobals: false }] }, { code: "var Object = 0;", options: [{ builtinGlobals: true }], parserOptions: { sourceType: "module" } }, { code: "var Object = 0;", options: [{ builtinGlobals: true }], parserOptions: { ecmaFeatures: { globalReturn: true } } }, - { code: "var top = 0;", env: { browser: true } }, { code: "var top = 0;", options: [{ builtinGlobals: true }] }, { code: "var top = 0;", options: [{ builtinGlobals: true }], parserOptions: { ecmaFeatures: { globalReturn: true } }, env: { browser: true } }, { code: "var top = 0;", options: [{ builtinGlobals: true }], parserOptions: { sourceType: "module" }, env: { browser: true } }, @@ -165,6 +163,20 @@ ruleTester.run("no-redeclare", rule, { ] }, + { + code: "var Object = 0;", + errors: [ + { message: "'Object' is already defined as a built-in global variable.", type: "Identifier" } + ] + }, + { + code: "var top = 0;", + env: { browser: true }, + errors: [ + { message: "'top' is already defined as a built-in global variable.", type: "Identifier" } + ] + }, + // let/const { code: "let a; let a;", From 9493cd49c6dc32cfecd218daa3e4b16bdf6421a7 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 15 Mar 2019 10:30:52 +0900 Subject: [PATCH 03/13] chore: fix test-case-property-ordering --- .eslintrc.js | 17 +++++++++- tests/lib/rules/array-bracket-spacing.js | 12 +++---- tests/lib/rules/comma-dangle.js | 16 ++++----- tests/lib/rules/indent.js | 28 ++++++++-------- tests/lib/rules/keyword-spacing.js | 4 +-- tests/lib/rules/no-console.js | 2 +- tests/lib/rules/no-eval.js | 16 ++++----- tests/lib/rules/no-global-assign.js | 10 +++--- tests/lib/rules/no-magic-numbers.js | 8 ++--- tests/lib/rules/no-mixed-spaces-and-tabs.js | 8 ++--- tests/lib/rules/no-native-reassign.js | 10 +++--- tests/lib/rules/no-restricted-globals.js | 36 ++++++++++----------- tests/lib/rules/no-sequences.js | 2 +- tests/lib/rules/no-shadow.js | 12 +++---- tests/lib/rules/no-unused-vars.js | 4 +-- tests/lib/rules/object-curly-newline.js | 16 ++++----- tests/lib/rules/object-curly-spacing.js | 4 +-- tests/lib/rules/prefer-const.js | 4 +-- tests/lib/rules/quote-props.js | 8 ++--- tests/lib/rules/require-unicode-regexp.js | 8 ++--- tests/lib/rules/space-infix-ops.js | 18 +++++------ 21 files changed, 129 insertions(+), 114 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 86cc480173f..ff213cf5f97 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -17,7 +17,22 @@ module.exports = { "eslint-plugin/prefer-placeholders": "error", "eslint-plugin/report-message-format": ["error", "[^a-z].*\\.$"], "eslint-plugin/require-meta-type": "error", - "eslint-plugin/test-case-property-ordering": "error", + "eslint-plugin/test-case-property-ordering": [ + "error", + + // https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/issues/79 + [ + "filename", + "code", + "output", + "options", + "parser", + "parserOptions", + "globals", + "env", + "errors" + ] + ], "eslint-plugin/test-case-shorthand-strings": "error", "internal-rules/multiline-comment-style": "error" }, diff --git a/tests/lib/rules/array-bracket-spacing.js b/tests/lib/rules/array-bracket-spacing.js index da3dadd6ec5..efb72ca5f52 100644 --- a/tests/lib/rules/array-bracket-spacing.js +++ b/tests/lib/rules/array-bracket-spacing.js @@ -174,8 +174,8 @@ ruleTester.run("array-bracket-spacing", rule, { { code: "var obj = {'foo': [1, 2]}", options: ["never"] }, // destructuring with type annotation - { code: "([ a, b ]: Array) => {}", options: ["always"], parserOptions: { ecmaVersion: 6 }, parser: parser("flow-destructuring-1") }, - { code: "([a, b]: Array< any >) => {}", options: ["never"], parserOptions: { ecmaVersion: 6 }, parser: parser("flow-destructuring-2") } + { code: "([ a, b ]: Array) => {}", options: ["always"], parser: parser("flow-destructuring-1"), parserOptions: { ecmaVersion: 6 } }, + { code: "([a, b]: Array< any >) => {}", options: ["never"], parser: parser("flow-destructuring-2"), parserOptions: { ecmaVersion: 6 } } ], invalid: [ @@ -830,6 +830,7 @@ ruleTester.run("array-bracket-spacing", rule, { code: "([ a, b ]: Array) => {}", output: "([a, b]: Array) => {}", options: ["never"], + parser: parser("flow-destructuring-1"), parserOptions: { ecmaVersion: 6 }, @@ -852,13 +853,13 @@ ruleTester.run("array-bracket-spacing", rule, { line: 1, column: 9 } - ], - parser: parser("flow-destructuring-1") + ] }, { code: "([a, b]: Array< any >) => {}", output: "([ a, b ]: Array< any >) => {}", options: ["always"], + parser: parser("flow-destructuring-2"), parserOptions: { ecmaVersion: 6 }, @@ -881,8 +882,7 @@ ruleTester.run("array-bracket-spacing", rule, { line: 1, column: 7 } - ], - parser: parser("flow-destructuring-2") + ] } ] }); diff --git a/tests/lib/rules/comma-dangle.js b/tests/lib/rules/comma-dangle.js index 8797dbdcf19..1f1fc33cf09 100644 --- a/tests/lib/rules/comma-dangle.js +++ b/tests/lib/rules/comma-dangle.js @@ -1410,29 +1410,29 @@ let d = 0;export {d,}; code: "function foo({a}: {a: string,}) {}", output: "function foo({a,}: {a: string,}) {}", options: ["always"], - errors: [{ messageId: "missing" }], - parser: parser("object-pattern-1") + parser: parser("object-pattern-1"), + errors: [{ messageId: "missing" }] }, { code: "function foo({a,}: {a: string}) {}", output: "function foo({a}: {a: string}) {}", options: ["never"], - errors: [{ messageId: "unexpected" }], - parser: parser("object-pattern-2") + parser: parser("object-pattern-2"), + errors: [{ messageId: "unexpected" }] }, { code: "function foo(a): {b: boolean,} {}", output: "function foo(a,): {b: boolean,} {}", options: [{ functions: "always" }], - errors: [{ messageId: "missing" }], - parser: parser("return-type-1") + parser: parser("return-type-1"), + errors: [{ messageId: "missing" }] }, { code: "function foo(a,): {b: boolean} {}", output: "function foo(a): {b: boolean} {}", options: [{ functions: "never" }], - errors: [{ messageId: "unexpected" }], - parser: parser("return-type-2") + parser: parser("return-type-2"), + errors: [{ messageId: "unexpected" }] } ] }); diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js index 45abc654d0e..7d5210b45ca 100644 --- a/tests/lib/rules/indent.js +++ b/tests/lib/rules/indent.js @@ -8686,8 +8686,8 @@ ruleTester.run("indent", rule, { } } `, - errors: expectedErrors([[3, 8, 4, "Identifier"], [6, 8, 4, "Keyword"]]), - parser: parser("unknown-nodes/namespace-invalid") + parser: parser("unknown-nodes/namespace-invalid"), + errors: expectedErrors([[3, 8, 4, "Identifier"], [6, 8, 4, "Keyword"]]) }, { code: unIndent` @@ -8718,8 +8718,8 @@ ruleTester.run("indent", rule, { } } `, - errors: expectedErrors([[4, 12, 8, "Identifier"], [7, 12, 8, "Identifier"], [10, 8, 4, "Identifier"]]), - parser: parser("unknown-nodes/abstract-class-invalid") + parser: parser("unknown-nodes/abstract-class-invalid"), + errors: expectedErrors([[4, 12, 8, "Identifier"], [7, 12, 8, "Identifier"], [10, 8, 4, "Identifier"]]) }, { code: unIndent` @@ -8748,14 +8748,14 @@ ruleTester.run("indent", rule, { } } `, + parser: parser("unknown-nodes/functions-with-abstract-class-invalid"), errors: expectedErrors([ [4, 12, 8, "Keyword"], [5, 16, 8, "Keyword"], [6, 20, 8, "Identifier"], [7, 16, 8, "Punctuator"], [8, 12, 8, "Punctuator"] - ]), - parser: parser("unknown-nodes/functions-with-abstract-class-invalid") + ]) }, { code: unIndent` @@ -8788,11 +8788,11 @@ ruleTester.run("indent", rule, { } } `, + parser: parser("unknown-nodes/namespace-with-functions-with-abstract-class-invalid"), errors: expectedErrors([ [3, 8, 4, "Keyword"], [7, 24, 20, "Identifier"] - ]), - parser: parser("unknown-nodes/namespace-with-functions-with-abstract-class-invalid") + ]) }, //---------------------------------------------------------------------- @@ -9369,8 +9369,8 @@ ruleTester.run("indent", rule, { foo }: bar) => baz `, - errors: expectedErrors([3, 0, 4, "Punctuator"]), - parser: require.resolve("../../fixtures/parsers/babel-eslint7/object-pattern-with-annotation") + parser: require.resolve("../../fixtures/parsers/babel-eslint7/object-pattern-with-annotation"), + errors: expectedErrors([3, 0, 4, "Punctuator"]) }, { code: unIndent` @@ -9383,8 +9383,8 @@ ruleTester.run("indent", rule, { foo ]: bar) => baz `, - errors: expectedErrors([3, 0, 4, "Punctuator"]), - parser: require.resolve("../../fixtures/parsers/babel-eslint7/array-pattern-with-annotation") + parser: require.resolve("../../fixtures/parsers/babel-eslint7/array-pattern-with-annotation"), + errors: expectedErrors([3, 0, 4, "Punctuator"]) }, { code: unIndent` @@ -9397,8 +9397,8 @@ ruleTester.run("indent", rule, { foo }: {}) => baz `, - errors: expectedErrors([3, 0, 4, "Punctuator"]), - parser: require.resolve("../../fixtures/parsers/babel-eslint7/object-pattern-with-object-annotation") + parser: require.resolve("../../fixtures/parsers/babel-eslint7/object-pattern-with-object-annotation"), + errors: expectedErrors([3, 0, 4, "Punctuator"]) }, { code: unIndent` diff --git a/tests/lib/rules/keyword-spacing.js b/tests/lib/rules/keyword-spacing.js index 5e6a0d83730..a2b7d2f8ac6 100644 --- a/tests/lib/rules/keyword-spacing.js +++ b/tests/lib/rules/keyword-spacing.js @@ -3135,8 +3135,8 @@ ruleTester.run("keyword-spacing", rule, { { code: "class Foo { @desc({set a(value) {}, get a() {}, async c() {}}) async[foo]() {} }", output: "class Foo { @desc({set a(value) {}, get a() {}, async c() {}}) async [foo]() {} }", - errors: expectedAfter("async"), - parser: parser("typescript-parsers/decorator-with-keywords-class-method") + parser: parser("typescript-parsers/decorator-with-keywords-class-method"), + errors: expectedAfter("async") } ] }); diff --git a/tests/lib/rules/no-console.js b/tests/lib/rules/no-console.js index 76a39227e5c..5241acc251b 100644 --- a/tests/lib/rules/no-console.js +++ b/tests/lib/rules/no-console.js @@ -58,6 +58,6 @@ ruleTester.run("no-console", rule, { { code: "console.warn(foo)", options: [{ allow: ["info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, // In case that implicit global variable of 'console' exists - { code: "console.log(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression" }], env: { node: true } } + { code: "console.log(foo)", env: { node: true }, errors: [{ messageId: "unexpected", type: "MemberExpression" }] } ] }); diff --git a/tests/lib/rules/no-eval.js b/tests/lib/rules/no-eval.js index d31c726b946..5421bbcc875 100644 --- a/tests/lib/rules/no-eval.js +++ b/tests/lib/rules/no-eval.js @@ -72,17 +72,17 @@ ruleTester.run("no-eval", rule, { // Indirect eval { code: "(0, eval)('foo')", errors: [{ messageId: "unexpected", type: "Identifier" }] }, - { code: "(0, window.eval)('foo')", errors: [{ messageId: "unexpected", type: "MemberExpression" }], env: { browser: true } }, - { code: "(0, window['eval'])('foo')", errors: [{ messageId: "unexpected", type: "MemberExpression" }], env: { browser: true } }, + { code: "(0, window.eval)('foo')", env: { browser: true }, errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "(0, window['eval'])('foo')", env: { browser: true }, errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, { code: "var EVAL = eval; EVAL('foo')", errors: [{ messageId: "unexpected", type: "Identifier" }] }, { code: "var EVAL = this.eval; EVAL('foo')", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, { code: "(function(exe){ exe('foo') })(eval);", errors: [{ messageId: "unexpected", type: "Identifier" }] }, - { code: "window.eval('foo')", errors: [{ messageId: "unexpected", type: "CallExpression" }], env: { browser: true } }, - { code: "window.window.eval('foo')", errors: [{ messageId: "unexpected", type: "CallExpression" }], env: { browser: true } }, - { code: "window.window['eval']('foo')", errors: [{ messageId: "unexpected", type: "CallExpression" }], env: { browser: true } }, - { code: "global.eval('foo')", errors: [{ messageId: "unexpected", type: "CallExpression" }], env: { node: true } }, - { code: "global.global.eval('foo')", errors: [{ messageId: "unexpected", type: "CallExpression" }], env: { node: true } }, - { code: "global.global[`eval`]('foo')", parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unexpected", type: "CallExpression" }], env: { node: true } }, + { code: "window.eval('foo')", env: { browser: true }, errors: [{ messageId: "unexpected", type: "CallExpression" }] }, + { code: "window.window.eval('foo')", env: { browser: true }, errors: [{ messageId: "unexpected", type: "CallExpression" }] }, + { code: "window.window['eval']('foo')", env: { browser: true }, errors: [{ messageId: "unexpected", type: "CallExpression" }] }, + { code: "global.eval('foo')", env: { node: true }, errors: [{ messageId: "unexpected", type: "CallExpression" }] }, + { code: "global.global.eval('foo')", env: { node: true }, errors: [{ messageId: "unexpected", type: "CallExpression" }] }, + { code: "global.global[`eval`]('foo')", parserOptions: { ecmaVersion: 6 }, env: { node: true }, errors: [{ messageId: "unexpected", type: "CallExpression" }] }, { code: "this.eval('foo')", errors: [{ messageId: "unexpected", type: "CallExpression" }] }, { code: "function foo() { this.eval('foo') }", errors: [{ messageId: "unexpected", type: "CallExpression" }] } ] diff --git a/tests/lib/rules/no-global-assign.js b/tests/lib/rules/no-global-assign.js index 3cd7926373e..051752db016 100644 --- a/tests/lib/rules/no-global-assign.js +++ b/tests/lib/rules/no-global-assign.js @@ -42,18 +42,18 @@ ruleTester.run("no-global-assign", rule, { }, { code: "top = 0;", - errors: [{ message: "Read-only global 'top' should not be modified.", type: "Identifier" }], - env: { browser: true } + env: { browser: true }, + errors: [{ message: "Read-only global 'top' should not be modified.", type: "Identifier" }] }, { code: "require = 0;", - errors: [{ message: "Read-only global 'require' should not be modified.", type: "Identifier" }], - env: { node: true } + env: { node: true }, + errors: [{ message: "Read-only global 'require' should not be modified.", type: "Identifier" }] }, // Notifications of readonly are moved from no-undef: https://github.com/eslint/eslint/issues/4504 { code: "/*global b:false*/ function f() { b = 1; }", errors: [{ message: "Read-only global 'b' should not be modified.", type: "Identifier" }] }, - { code: "function f() { b = 1; }", errors: [{ message: "Read-only global 'b' should not be modified.", type: "Identifier" }], globals: { b: false } }, + { code: "function f() { b = 1; }", globals: { b: false }, errors: [{ message: "Read-only global 'b' should not be modified.", type: "Identifier" }] }, { code: "/*global b:false*/ function f() { b++; }", errors: [{ message: "Read-only global 'b' should not be modified.", type: "Identifier" }] }, { code: "/*global b*/ b = 1;", errors: [{ message: "Read-only global 'b' should not be modified.", type: "Identifier" }] }, { code: "Array = 1;", errors: [{ message: "Read-only global 'Array' should not be modified.", type: "Identifier" }] } diff --git a/tests/lib/rules/no-magic-numbers.js b/tests/lib/rules/no-magic-numbers.js index 34b1e2f6ae2..beb0282dbbd 100644 --- a/tests/lib/rules/no-magic-numbers.js +++ b/tests/lib/rules/no-magic-numbers.js @@ -83,8 +83,8 @@ ruleTester.run("no-magic-numbers", rule, { options: [{ enforceConst: true }], - errors: [{ messageId: "useConst" }], - env: { es6: true } + env: { es6: true }, + errors: [{ messageId: "useConst" }] }, { code: "var foo = 0 + 1;", @@ -189,6 +189,7 @@ ruleTester.run("no-magic-numbers", rule, { "function invokeInTen(func) {\n" + "setTimeout(func, 10);\n" + "}\n", + env: { es6: true }, errors: [ { messageId: "noMagic", data: { raw: "10" }, line: 7 }, { messageId: "noMagic", data: { raw: "10" }, line: 7 }, @@ -196,8 +197,7 @@ ruleTester.run("no-magic-numbers", rule, { { messageId: "noMagic", data: { raw: "1000" }, line: 15 }, { messageId: "noMagic", data: { raw: "0" }, line: 19 }, { messageId: "noMagic", data: { raw: "10" }, line: 22 } - ], - env: { es6: true } + ] }, { code: "var data = ['foo', 'bar', 'baz']; var third = data[3];", diff --git a/tests/lib/rules/no-mixed-spaces-and-tabs.js b/tests/lib/rules/no-mixed-spaces-and-tabs.js index f427a6f1c2d..8badf704a16 100644 --- a/tests/lib/rules/no-mixed-spaces-and-tabs.js +++ b/tests/lib/rules/no-mixed-spaces-and-tabs.js @@ -136,6 +136,7 @@ ruleTester.run("no-mixed-spaces-and-tabs", rule, { { code: "`foo${\n \t 5 }bar`;", options: ["smart-tabs"], + env: { es6: true }, errors: [ { message: "Mixed spaces and tabs.", @@ -143,11 +144,11 @@ ruleTester.run("no-mixed-spaces-and-tabs", rule, { line: 2, column: 2 } - ], - env: { es6: true } + ] }, { code: "`foo${\n\t 5 }bar`;", + env: { es6: true }, errors: [ { message: "Mixed spaces and tabs.", @@ -155,8 +156,7 @@ ruleTester.run("no-mixed-spaces-and-tabs", rule, { line: 2, column: 2 } - ], - env: { es6: true } + ] } ] }); diff --git a/tests/lib/rules/no-native-reassign.js b/tests/lib/rules/no-native-reassign.js index ce6f4756f8f..eaf763bb74b 100644 --- a/tests/lib/rules/no-native-reassign.js +++ b/tests/lib/rules/no-native-reassign.js @@ -43,18 +43,18 @@ ruleTester.run("no-native-reassign", rule, { }, { code: "top = 0;", - errors: [{ message: "Read-only global 'top' should not be modified.", type: "Identifier" }], - env: { browser: true } + env: { browser: true }, + errors: [{ message: "Read-only global 'top' should not be modified.", type: "Identifier" }] }, { code: "require = 0;", - errors: [{ message: "Read-only global 'require' should not be modified.", type: "Identifier" }], - env: { node: true } + env: { node: true }, + errors: [{ message: "Read-only global 'require' should not be modified.", type: "Identifier" }] }, // Notifications of readonly are moved from no-undef: https://github.com/eslint/eslint/issues/4504 { code: "/*global b:false*/ function f() { b = 1; }", errors: [{ message: "Read-only global 'b' should not be modified.", type: "Identifier" }] }, - { code: "function f() { b = 1; }", errors: [{ message: "Read-only global 'b' should not be modified.", type: "Identifier" }], globals: { b: false } }, + { code: "function f() { b = 1; }", globals: { b: false }, errors: [{ message: "Read-only global 'b' should not be modified.", type: "Identifier" }] }, { code: "/*global b:false*/ function f() { b++; }", errors: [{ message: "Read-only global 'b' should not be modified.", type: "Identifier" }] }, { code: "/*global b*/ b = 1;", errors: [{ message: "Read-only global 'b' should not be modified.", type: "Identifier" }] }, { code: "Array = 1;", errors: [{ message: "Read-only global 'Array' should not be modified.", type: "Identifier" }] } diff --git a/tests/lib/rules/no-restricted-globals.js b/tests/lib/rules/no-restricted-globals.js index 8d4a6994179..0e09be16984 100644 --- a/tests/lib/rules/no-restricted-globals.js +++ b/tests/lib/rules/no-restricted-globals.js @@ -70,20 +70,20 @@ ruleTester.run("no-restricted-globals", rule, { { code: "function fn() { foo; }", options: ["foo"], - errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }], - globals: { foo: false } + globals: { foo: false }, + errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] }, { code: "event", options: ["foo", "event"], - errors: [{ message: "Unexpected use of 'event'.", type: "Identifier" }], - env: { browser: true } + env: { browser: true }, + errors: [{ message: "Unexpected use of 'event'.", type: "Identifier" }] }, { code: "foo", options: ["foo"], - errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }], - globals: { foo: false } + globals: { foo: false }, + errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] }, { code: "foo()", @@ -108,20 +108,20 @@ ruleTester.run("no-restricted-globals", rule, { { code: "function fn() { foo; }", options: [{ name: "foo" }], - errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }], - globals: { foo: false } + globals: { foo: false }, + errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] }, { code: "event", options: ["foo", { name: "event" }], - errors: [{ message: "Unexpected use of 'event'.", type: "Identifier" }], - env: { browser: true } + env: { browser: true }, + errors: [{ message: "Unexpected use of 'event'.", type: "Identifier" }] }, { code: "foo", options: [{ name: "foo" }], - errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }], - globals: { foo: false } + globals: { foo: false }, + errors: [{ message: "Unexpected use of 'foo'.", type: "Identifier" }] }, { code: "foo()", @@ -146,20 +146,20 @@ ruleTester.run("no-restricted-globals", rule, { { code: "function fn() { foo; }", options: [{ name: "foo", message: "Use bar instead." }], - errors: [{ message: "Unexpected use of 'foo'. Use bar instead.", type: "Identifier" }], - globals: { foo: false } + globals: { foo: false }, + errors: [{ message: "Unexpected use of 'foo'. Use bar instead.", type: "Identifier" }] }, { code: "event", options: ["foo", { name: "event", message: "Use local event parameter." }], - errors: [{ message: "Unexpected use of 'event'. Use local event parameter.", type: "Identifier" }], - env: { browser: true } + env: { browser: true }, + errors: [{ message: "Unexpected use of 'event'. Use local event parameter.", type: "Identifier" }] }, { code: "foo", options: [{ name: "foo", message: "Use bar instead." }], - errors: [{ message: "Unexpected use of 'foo'. Use bar instead.", type: "Identifier" }], - globals: { foo: false } + globals: { foo: false }, + errors: [{ message: "Unexpected use of 'foo'. Use bar instead.", type: "Identifier" }] }, { code: "foo()", diff --git a/tests/lib/rules/no-sequences.js b/tests/lib/rules/no-sequences.js index 1ab5ed42e70..a32400b2f1a 100644 --- a/tests/lib/rules/no-sequences.js +++ b/tests/lib/rules/no-sequences.js @@ -61,6 +61,6 @@ ruleTester.run("no-sequences", rule, { { code: "switch (doSomething(), val) {}", errors: errors(22) }, { code: "while (doSomething(), !!test);", errors: errors(21) }, { code: "with (doSomething(), val) {}", errors: errors(20) }, - { code: "a => (doSomething(), a)", errors: errors(20), env: { es6: true } } + { code: "a => (doSomething(), a)", env: { es6: true }, errors: errors(20) } ] }); diff --git a/tests/lib/rules/no-shadow.js b/tests/lib/rules/no-shadow.js index 071eadd1530..18ce5b16197 100644 --- a/tests/lib/rules/no-shadow.js +++ b/tests/lib/rules/no-shadow.js @@ -299,8 +299,8 @@ ruleTester.run("no-shadow", rule, { { code: "function foo() { var top = 0; }", options: [{ builtinGlobals: true }], - errors: [{ message: "'top' is already declared in the upper scope.", type: "Identifier" }], - env: { browser: true } + env: { browser: true }, + errors: [{ message: "'top' is already declared in the upper scope.", type: "Identifier" }] }, { code: "var Object = 0;", @@ -312,8 +312,8 @@ ruleTester.run("no-shadow", rule, { code: "var top = 0;", options: [{ builtinGlobals: true }], parserOptions: { sourceType: "module" }, - errors: [{ message: "'top' is already declared in the upper scope.", type: "Identifier" }], - env: { browser: true } + env: { browser: true }, + errors: [{ message: "'top' is already declared in the upper scope.", type: "Identifier" }] }, { code: "var Object = 0;", @@ -325,8 +325,8 @@ ruleTester.run("no-shadow", rule, { code: "var top = 0;", options: [{ builtinGlobals: true }], parserOptions: { ecmaFeatures: { globalReturn: true } }, - errors: [{ message: "'top' is already declared in the upper scope.", type: "Identifier" }], - env: { browser: true } + env: { browser: true }, + errors: [{ message: "'top' is already declared in the upper scope.", type: "Identifier" }] }, { code: "function foo(cb) { (function (cb) { cb(42); })(cb); }", diff --git a/tests/lib/rules/no-unused-vars.js b/tests/lib/rules/no-unused-vars.js index 9b6733cacc3..3012dec6368 100644 --- a/tests/lib/rules/no-unused-vars.js +++ b/tests/lib/rules/no-unused-vars.js @@ -477,10 +477,10 @@ ruleTester.run("no-unused-vars", rule, { // surrogate pair. { code: "/*global 𠮷𩸽, 𠮷*/\n\\u{20BB7}\\u{29E3D};", + env: { es6: true }, errors: [ { line: 1, column: 16, message: "'𠮷' is defined but never used." } - ], - env: { es6: true } + ] }, // https://github.com/eslint/eslint/issues/4047 diff --git a/tests/lib/rules/object-curly-newline.js b/tests/lib/rules/object-curly-newline.js index 072ca1611b3..8cf75265a5e 100644 --- a/tests/lib/rules/object-curly-newline.js +++ b/tests/lib/rules/object-curly-newline.js @@ -670,11 +670,11 @@ ruleTester.run("object-curly-newline", rule, { "} : MyType) {}" ].join("\n"), options: ["always"], + parser: resolvePath(__dirname, "../../fixtures/parsers/object-curly-newline/flow-stub-parser-singleline"), errors: [ { line: 1, column: 14, message: "Expected a line break after this opening brace." }, { line: 1, column: 21, message: "Expected a line break before this closing brace." } - ], - parser: resolvePath(__dirname, "../../fixtures/parsers/object-curly-newline/flow-stub-parser-singleline") + ] }, { code: "function foo({ a, b } : { a : string, b : string }) {}", @@ -684,11 +684,11 @@ ruleTester.run("object-curly-newline", rule, { "} : { a : string, b : string }) {}" ].join("\n"), options: ["always"], + parser: resolvePath(__dirname, "../../fixtures/parsers/object-curly-newline/flow-stub-parser-singleline-type-literal"), errors: [ { line: 1, column: 14, message: "Expected a line break after this opening brace." }, { line: 1, column: 21, message: "Expected a line break before this closing brace." } - ], - parser: resolvePath(__dirname, "../../fixtures/parsers/object-curly-newline/flow-stub-parser-singleline-type-literal") + ] }, // "never" ------------------------------------------------------------ @@ -784,11 +784,11 @@ ruleTester.run("object-curly-newline", rule, { " b} : MyType) {}" ].join("\n"), options: ["never"], + parser: resolvePath(__dirname, "../../fixtures/parsers/object-curly-newline/flow-stub-parser-multiline"), errors: [ { line: 1, column: 14, message: "Unexpected line break after this opening brace." }, { line: 4, column: 1, message: "Unexpected line break before this closing brace." } - ], - parser: resolvePath(__dirname, "../../fixtures/parsers/object-curly-newline/flow-stub-parser-multiline") + ] }, { code: [ @@ -802,11 +802,11 @@ ruleTester.run("object-curly-newline", rule, { " b} : { a : string, b : string }) {}" ].join("\n"), options: ["never"], + parser: resolvePath(__dirname, "../../fixtures/parsers/object-curly-newline/flow-stub-parser-multiline-type-literal"), errors: [ { line: 1, column: 14, message: "Unexpected line break after this opening brace." }, { line: 4, column: 1, message: "Unexpected line break before this closing brace." } - ], - parser: resolvePath(__dirname, "../../fixtures/parsers/object-curly-newline/flow-stub-parser-multiline-type-literal") + ] }, // "multiline" --------------------------------------------------------- diff --git a/tests/lib/rules/object-curly-spacing.js b/tests/lib/rules/object-curly-spacing.js index c016b731b16..3595ff6b523 100644 --- a/tests/lib/rules/object-curly-spacing.js +++ b/tests/lib/rules/object-curly-spacing.js @@ -768,13 +768,13 @@ ruleTester.run("object-curly-spacing", rule, { code: "function foo ({a, b }: Props) {\n}", output: "function foo ({a, b}: Props) {\n}", options: ["never"], + parser: resolvePath(__dirname, "../../fixtures/parsers/object-curly-spacing/flow-stub-parser-never-invalid"), errors: [ { message: "There should be no space before '}'.", type: "ObjectPattern" } - ], - parser: resolvePath(__dirname, "../../fixtures/parsers/object-curly-spacing/flow-stub-parser-never-invalid") + ] } ] }); diff --git a/tests/lib/rules/prefer-const.js b/tests/lib/rules/prefer-const.js index 699feb6cfb2..66eec8c539f 100644 --- a/tests/lib/rules/prefer-const.js +++ b/tests/lib/rules/prefer-const.js @@ -383,8 +383,8 @@ ruleTester.run("prefer-const", rule, { code: "let { name, ...otherStuff } = obj; otherStuff = {};", output: null, options: [{ destructuring: "any" }], - errors: [{ messageId: "useConst", data: { name: "name" }, type: "Identifier", column: 7 }], - parser: fixtureParser("babel-eslint5/destructuring-object-spread") + parser: fixtureParser("babel-eslint5/destructuring-object-spread"), + errors: [{ messageId: "useConst", data: { name: "name" }, type: "Identifier", column: 7 }] }, // Warnings are located at declaration if there are reading references before assignments. diff --git a/tests/lib/rules/quote-props.js b/tests/lib/rules/quote-props.js index 6291960d351..fa8037b7c32 100644 --- a/tests/lib/rules/quote-props.js +++ b/tests/lib/rules/quote-props.js @@ -148,18 +148,18 @@ ruleTester.run("quote-props", rule, { code: "({ 'a': 0, [x]: 0 })", output: "({ a: 0, [x]: 0 })", options: ["consistent-as-needed"], + env: { es6: true }, errors: [ { message: "Properties shouldn't be quoted as all quotes are redundant.", type: "Property" } - ], - env: { es6: true } + ] }, { code: "({ 'a': 0, x })", output: "({ a: 0, x })", options: ["consistent-as-needed"], + env: { es6: true }, errors: [{ message: "Properties shouldn't be quoted as all quotes are redundant.", type: "Property" - }], - env: { es6: true } + }] }, { code: "({ 'true': 0, 'null': 0 })", output: "({ true: 0, null: 0 })", diff --git a/tests/lib/rules/require-unicode-regexp.js b/tests/lib/rules/require-unicode-regexp.js index a261c86e3b2..b6ac758d17d 100644 --- a/tests/lib/rules/require-unicode-regexp.js +++ b/tests/lib/rules/require-unicode-regexp.js @@ -78,13 +78,13 @@ ruleTester.run("require-unicode-regexp", rule, { }, { code: "new window.RegExp('foo')", - errors: [{ messageId: "requireUFlag" }], - env: { browser: true } + env: { browser: true }, + errors: [{ messageId: "requireUFlag" }] }, { code: "new global.RegExp('foo')", - errors: [{ messageId: "requireUFlag" }], - env: { node: true } + env: { node: true }, + errors: [{ messageId: "requireUFlag" }] } ] }); diff --git a/tests/lib/rules/space-infix-ops.js b/tests/lib/rules/space-infix-ops.js index 76939f7fe5b..8932997e25a 100644 --- a/tests/lib/rules/space-infix-ops.js +++ b/tests/lib/rules/space-infix-ops.js @@ -41,13 +41,13 @@ ruleTester.run("space-infix-ops", rule, { { code: "a |0", options: [{ int32Hint: true }] }, // Type Annotations - { code: "function foo(a: number = 0) { }", parserOptions: { ecmaVersion: 6 }, parser: parser("type-annotations/function-parameter-type-annotation") }, - { code: "function foo(): Bar { }", parserOptions: { ecmaVersion: 6 }, parser: parser("type-annotations/function-return-type-annotation") }, - { code: "var foo: Bar = '';", parserOptions: { ecmaVersion: 6 }, parser: parser("type-annotations/variable-declaration-init-type-annotation") }, - { code: "const foo = function(a: number = 0): Bar { };", parserOptions: { ecmaVersion: 6 }, parser: parser("type-annotations/function-expression-type-annotation") }, + { code: "function foo(a: number = 0) { }", parser: parser("type-annotations/function-parameter-type-annotation"), parserOptions: { ecmaVersion: 6 } }, + { code: "function foo(): Bar { }", parser: parser("type-annotations/function-return-type-annotation"), parserOptions: { ecmaVersion: 6 } }, + { code: "var foo: Bar = '';", parser: parser("type-annotations/variable-declaration-init-type-annotation"), parserOptions: { ecmaVersion: 6 } }, + { code: "const foo = function(a: number = 0): Bar { };", parser: parser("type-annotations/function-expression-type-annotation"), parserOptions: { ecmaVersion: 6 } }, // TypeScript Type Aliases - { code: "type Foo = T;", parserOptions: { ecmaVersion: 6 }, parser: parser("typescript-parsers/type-alias") } + { code: "type Foo = T;", parser: parser("typescript-parsers/type-alias"), parserOptions: { ecmaVersion: 6 } } ], invalid: [ { @@ -368,25 +368,25 @@ ruleTester.run("space-infix-ops", rule, { { code: "var a: Foo= b;", output: "var a: Foo = b;", + parser: parser("type-annotations/variable-declaration-init-type-annotation-no-space"), errors: [{ message: "Operator '=' must be spaced.", type: "VariableDeclarator", line: 1, column: 11 - }], - parser: parser("type-annotations/variable-declaration-init-type-annotation-no-space") + }] }, { code: "function foo(a: number=0): Foo { }", output: "function foo(a: number = 0): Foo { }", + parser: parser("type-annotations/function-declaration-type-annotation-no-space"), parserOptions: { ecmaVersion: 6 }, errors: [{ message: "Operator '=' must be spaced.", line: 1, column: 23, nodeType: "AssignmentPattern" - }], - parser: parser("type-annotations/function-declaration-type-annotation-no-space") + }] } ] }); From 9b05f8abe3061902318190eae70103bbd50814e5 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 15 Mar 2019 10:42:58 +0900 Subject: [PATCH 04/13] fix engines.node to use Object.entries --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6f4fad5f9f4..9933878a06f 100644 --- a/package.json +++ b/package.json @@ -124,6 +124,6 @@ ], "license": "MIT", "engines": { - "node": "^6.14.0 || ^8.10.0 || >=9.10.0" + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" } } From 2fc6a2d1ecccaf1b03c4b29f9d83471ef424af1a Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 15 Mar 2019 11:38:45 +0900 Subject: [PATCH 05/13] update documents --- docs/developer-guide/working-with-rules.md | 7 +++ docs/rules/no-redeclare.md | 2 +- docs/user-guide/migrating-to-6.0.0.md | 52 ++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 docs/user-guide/migrating-to-6.0.0.md diff --git a/docs/developer-guide/working-with-rules.md b/docs/developer-guide/working-with-rules.md index d189d53ad4f..b7c81a21f65 100644 --- a/docs/developer-guide/working-with-rules.md +++ b/docs/developer-guide/working-with-rules.md @@ -169,6 +169,13 @@ This method returns the scope which has the following types: **※2** Only if the `for` statement defines the iteration variable as a block-scoped variable (E.g., `for (let i = 0;;) {}`).
**※3** The scope of the closest ancestor node which has own scope. If the closest ancestor node has multiple scopes then it chooses the innermost scope (E.g., the `Program` node has a `global` scope and a `module` scope if `Program#sourceType` is `"module"`. The innermost scope is the `module` scope.). +The returned value is a `Scope` object that [`eslint-scope` package defined](scope-manager-interface.md). The `Variable` objects of global variables have some additional properties. + +- `variable.writeable` (`boolean`) ... If `true`, this global variable can be assigned arbitrary value. Otherwise, this global variable is read-only. +- `variable.eslintExplicitGlobal` (`boolean`) ... If `true`, this global variable was defined by a `/* globals */` directive comment in the source code file. +- `variable.eslintExplicitGlobalComments` (`Comment[]`) ... The array of `/* globals */` directive comments which defined this global variable in the source code file. +- `variable.eslintImplicitGlobalSetting` (`"readonly" | "writable" | undefined`) ... The configured value in config files. This can be different from `variable.writeable` if there are `/* globals */` directive comments. + ### context.report() The main method you'll use is `context.report()`, which publishes a warning or error (depending on the configuration being used). This method accepts a single argument, which is an object containing the following properties: diff --git a/docs/rules/no-redeclare.md b/docs/rules/no-redeclare.md index d0d71e5dee5..c3b8422cf3f 100644 --- a/docs/rules/no-redeclare.md +++ b/docs/rules/no-redeclare.md @@ -27,7 +27,7 @@ a = 10; ## Options -This rule takes one optional argument, an object with a boolean property `"builtinGlobals"`. It defaults to `false`. +This rule takes one optional argument, an object with a boolean property `"builtinGlobals"`. It defaults to `true`. If set to `true`, this rule also checks redeclaration of built-in globals, such as `Object`, `Array`, `Number`... ### builtinGlobals diff --git a/docs/user-guide/migrating-to-6.0.0.md b/docs/user-guide/migrating-to-6.0.0.md new file mode 100644 index 00000000000..e343ba8482b --- /dev/null +++ b/docs/user-guide/migrating-to-6.0.0.md @@ -0,0 +1,52 @@ +# Migrating to v6.0.0 + +ESLint v6.0.0 is the fifth major version release. We have made a few breaking changes in this release, but we expect that most users will be able to upgrade without any modifications to their build. This guide is intended to walk you through the breaking changes. + +The lists below are ordered roughly by the number of users each change is expected to affect, where the first items are expected to affect the most users. + +### Breaking changes for users + +- [`no-redeclare` rule now checks the redeclarations by `/* globals */` directive comments](#no-redeclare-and-comments) +- [`no-redeclare` rule now checks the redeclarations with built-in globals by default](#no-redeclare-and-builtins) + +### Breaking changes for plugin/custom rule developers + +- [`variable.eslintExplicitGlobalComment` property was removed](#remove-variable-explicit-global-comment) + +### Breaking changes for integration developers + +--- + + + +## `no-redeclare` rule now checks the redeclarations by `/* globals */` directive comments + +[no-redeclare] rule reports the following cases newly: + +- Your config file defined a global variable but there is `/* globals */` directive comment of the defined global variable in your source code. +- There are multiple `/* globals */` directive comments for the same variable. + +**To address:** Please remove the redundant `/* globals */` directive comments. + +## `no-redeclare` rule now checks the redeclarations with built-in globals by default + +The `builtinGlobals` option of [no-redeclare] rule is `true` by default. Previously, it was `false` by default. + +**To address:** Please remove the redundant declarations or disable `builtinGlobals` option manually. + +--- + + + +## `variable.eslintExplicitGlobalComment` property was removed + +Undocumented `variable.eslintExplicitGlobalComment` property, only [no-unused-vars] rule had used it, was removed. + +**To address:** Please use [`variable.eslintExplicitGlobalComments` property](../working-with-rules.md#contextgetscope) instead. + +--- + + + +[no-redeclare]: https://eslint.org/docs/rules/no-redeclare +[no-unused-vars]: https://eslint.org/docs/rules/no-unused-vars From 012bd526d54347a99cb63fcd65171d26bbd4850a Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 15 Mar 2019 11:49:24 +0900 Subject: [PATCH 06/13] fix markdownlint --- docs/developer-guide/working-with-rules.md | 8 ++++---- docs/user-guide/migrating-to-6.0.0.md | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/developer-guide/working-with-rules.md b/docs/developer-guide/working-with-rules.md index b7c81a21f65..95df8a301db 100644 --- a/docs/developer-guide/working-with-rules.md +++ b/docs/developer-guide/working-with-rules.md @@ -171,10 +171,10 @@ This method returns the scope which has the following types: The returned value is a `Scope` object that [`eslint-scope` package defined](scope-manager-interface.md). The `Variable` objects of global variables have some additional properties. -- `variable.writeable` (`boolean`) ... If `true`, this global variable can be assigned arbitrary value. Otherwise, this global variable is read-only. -- `variable.eslintExplicitGlobal` (`boolean`) ... If `true`, this global variable was defined by a `/* globals */` directive comment in the source code file. -- `variable.eslintExplicitGlobalComments` (`Comment[]`) ... The array of `/* globals */` directive comments which defined this global variable in the source code file. -- `variable.eslintImplicitGlobalSetting` (`"readonly" | "writable" | undefined`) ... The configured value in config files. This can be different from `variable.writeable` if there are `/* globals */` directive comments. +* `variable.writeable` (`boolean`) ... If `true`, this global variable can be assigned arbitrary value. Otherwise, this global variable is read-only. +* `variable.eslintExplicitGlobal` (`boolean`) ... If `true`, this global variable was defined by a `/* globals */` directive comment in the source code file. +* `variable.eslintExplicitGlobalComments` (`Comment[]`) ... The array of `/* globals */` directive comments which defined this global variable in the source code file. +* `variable.eslintImplicitGlobalSetting` (`"readonly" | "writable" | undefined`) ... The configured value in config files. This can be different from `variable.writeable` if there are `/* globals */` directive comments. ### context.report() diff --git a/docs/user-guide/migrating-to-6.0.0.md b/docs/user-guide/migrating-to-6.0.0.md index e343ba8482b..74a4d6849bc 100644 --- a/docs/user-guide/migrating-to-6.0.0.md +++ b/docs/user-guide/migrating-to-6.0.0.md @@ -6,12 +6,12 @@ The lists below are ordered roughly by the number of users each change is expect ### Breaking changes for users -- [`no-redeclare` rule now checks the redeclarations by `/* globals */` directive comments](#no-redeclare-and-comments) -- [`no-redeclare` rule now checks the redeclarations with built-in globals by default](#no-redeclare-and-builtins) +* [`no-redeclare` rule now checks the redeclarations by `/* globals */` directive comments](#no-redeclare-and-comments) +* [`no-redeclare` rule now checks the redeclarations with built-in globals by default](#no-redeclare-and-builtins) ### Breaking changes for plugin/custom rule developers -- [`variable.eslintExplicitGlobalComment` property was removed](#remove-variable-explicit-global-comment) +* [`variable.eslintExplicitGlobalComment` property was removed](#remove-variable-explicit-global-comment) ### Breaking changes for integration developers @@ -23,8 +23,8 @@ The lists below are ordered roughly by the number of users each change is expect [no-redeclare] rule reports the following cases newly: -- Your config file defined a global variable but there is `/* globals */` directive comment of the defined global variable in your source code. -- There are multiple `/* globals */` directive comments for the same variable. +* Your config file defined a global variable but there is `/* globals */` directive comment of the defined global variable in your source code. +* There are multiple `/* globals */` directive comments for the same variable. **To address:** Please remove the redundant `/* globals */` directive comments. From 746f7cc4ba2bf5630a1e73a8e972b8d2a03d4451 Mon Sep 17 00:00:00 2001 From: Teddy Katz Date: Fri, 15 Mar 2019 13:29:38 +0900 Subject: [PATCH 07/13] remove unnecessary compare Co-Authored-By: mysticatea --- lib/rules/no-redeclare.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/no-redeclare.js b/lib/rules/no-redeclare.js index 180f0261350..6adde0f97a5 100644 --- a/lib/rules/no-redeclare.js +++ b/lib/rules/no-redeclare.js @@ -47,7 +47,7 @@ module.exports = { const options = { builtinGlobals: Boolean( context.options.length === 0 || - context.options[0].builtinGlobals !== false + context.options[0].builtinGlobals ) }; const sourceCode = context.getSourceCode(); From 077b7ea251a858ae154bf35b03f9748ba7299172 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 15 Mar 2019 13:44:28 +0900 Subject: [PATCH 08/13] remove redundant comparison --- lib/rules/no-unused-vars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/no-unused-vars.js b/lib/rules/no-unused-vars.js index ce8f6cd8fbf..fe999182fb1 100644 --- a/lib/rules/no-unused-vars.js +++ b/lib/rules/no-unused-vars.js @@ -609,7 +609,7 @@ module.exports = { }); // If there are no regular declaration, report the first `/*globals*/` comment directive. - } else if (unusedVar.eslintExplicitGlobalComments && unusedVar.eslintExplicitGlobalComments.length > 0) { + } else if (unusedVar.eslintExplicitGlobalComments) { const directiveComment = unusedVar.eslintExplicitGlobalComments[0]; context.report({ From adb28a2409ddacaa813aa9fd4a79ddaa6e134742 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 15 Mar 2019 13:44:50 +0900 Subject: [PATCH 09/13] remove migration guide --- docs/user-guide/migrating-to-6.0.0.md | 52 --------------------------- 1 file changed, 52 deletions(-) delete mode 100644 docs/user-guide/migrating-to-6.0.0.md diff --git a/docs/user-guide/migrating-to-6.0.0.md b/docs/user-guide/migrating-to-6.0.0.md deleted file mode 100644 index 74a4d6849bc..00000000000 --- a/docs/user-guide/migrating-to-6.0.0.md +++ /dev/null @@ -1,52 +0,0 @@ -# Migrating to v6.0.0 - -ESLint v6.0.0 is the fifth major version release. We have made a few breaking changes in this release, but we expect that most users will be able to upgrade without any modifications to their build. This guide is intended to walk you through the breaking changes. - -The lists below are ordered roughly by the number of users each change is expected to affect, where the first items are expected to affect the most users. - -### Breaking changes for users - -* [`no-redeclare` rule now checks the redeclarations by `/* globals */` directive comments](#no-redeclare-and-comments) -* [`no-redeclare` rule now checks the redeclarations with built-in globals by default](#no-redeclare-and-builtins) - -### Breaking changes for plugin/custom rule developers - -* [`variable.eslintExplicitGlobalComment` property was removed](#remove-variable-explicit-global-comment) - -### Breaking changes for integration developers - ---- - - - -## `no-redeclare` rule now checks the redeclarations by `/* globals */` directive comments - -[no-redeclare] rule reports the following cases newly: - -* Your config file defined a global variable but there is `/* globals */` directive comment of the defined global variable in your source code. -* There are multiple `/* globals */` directive comments for the same variable. - -**To address:** Please remove the redundant `/* globals */` directive comments. - -## `no-redeclare` rule now checks the redeclarations with built-in globals by default - -The `builtinGlobals` option of [no-redeclare] rule is `true` by default. Previously, it was `false` by default. - -**To address:** Please remove the redundant declarations or disable `builtinGlobals` option manually. - ---- - - - -## `variable.eslintExplicitGlobalComment` property was removed - -Undocumented `variable.eslintExplicitGlobalComment` property, only [no-unused-vars] rule had used it, was removed. - -**To address:** Please use [`variable.eslintExplicitGlobalComments` property](../working-with-rules.md#contextgetscope) instead. - ---- - - - -[no-redeclare]: https://eslint.org/docs/rules/no-redeclare -[no-unused-vars]: https://eslint.org/docs/rules/no-unused-vars From f5df804492a8c974fa7abee147f0550e20ce3e93 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 15 Mar 2019 13:56:50 +0900 Subject: [PATCH 10/13] update working-with-rules.md --- docs/developer-guide/working-with-rules.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/developer-guide/working-with-rules.md b/docs/developer-guide/working-with-rules.md index 95df8a301db..581bbc442c5 100644 --- a/docs/developer-guide/working-with-rules.md +++ b/docs/developer-guide/working-with-rules.md @@ -171,9 +171,9 @@ This method returns the scope which has the following types: The returned value is a `Scope` object that [`eslint-scope` package defined](scope-manager-interface.md). The `Variable` objects of global variables have some additional properties. -* `variable.writeable` (`boolean`) ... If `true`, this global variable can be assigned arbitrary value. Otherwise, this global variable is read-only. -* `variable.eslintExplicitGlobal` (`boolean`) ... If `true`, this global variable was defined by a `/* globals */` directive comment in the source code file. -* `variable.eslintExplicitGlobalComments` (`Comment[]`) ... The array of `/* globals */` directive comments which defined this global variable in the source code file. +* `variable.writeable` (`boolean | undefined`) ... If `true`, this global variable can be assigned arbitrary value. If `false`, this global variable is read-only. +* `variable.eslintExplicitGlobal` (`boolean | undefined`) ... If `true`, this global variable was defined by a `/* globals */` directive comment in the source code file. +* `variable.eslintExplicitGlobalComments` (`Comment[] | undefined`) ... The array of `/* globals */` directive comments which defined this global variable in the source code file. This property is `undefined` if there are no `/* globals */` directive comments. * `variable.eslintImplicitGlobalSetting` (`"readonly" | "writable" | undefined`) ... The configured value in config files. This can be different from `variable.writeable` if there are `/* globals */` directive comments. ### context.report() From 3e540e8b7ef326402fcd34fcac85c2af592c6289 Mon Sep 17 00:00:00 2001 From: Kevin Partington Date: Fri, 29 Mar 2019 09:32:47 +0900 Subject: [PATCH 11/13] Update docs/developer-guide/working-with-rules.md Co-Authored-By: mysticatea --- docs/developer-guide/working-with-rules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer-guide/working-with-rules.md b/docs/developer-guide/working-with-rules.md index 581bbc442c5..c49863501ae 100644 --- a/docs/developer-guide/working-with-rules.md +++ b/docs/developer-guide/working-with-rules.md @@ -169,7 +169,7 @@ This method returns the scope which has the following types: **※2** Only if the `for` statement defines the iteration variable as a block-scoped variable (E.g., `for (let i = 0;;) {}`).
**※3** The scope of the closest ancestor node which has own scope. If the closest ancestor node has multiple scopes then it chooses the innermost scope (E.g., the `Program` node has a `global` scope and a `module` scope if `Program#sourceType` is `"module"`. The innermost scope is the `module` scope.). -The returned value is a `Scope` object that [`eslint-scope` package defined](scope-manager-interface.md). The `Variable` objects of global variables have some additional properties. +The returned value is a [`Scope` object](scope-manager-interface.md) defined by the `eslint-scope` package. The `Variable` objects of global variables have some additional properties. * `variable.writeable` (`boolean | undefined`) ... If `true`, this global variable can be assigned arbitrary value. If `false`, this global variable is read-only. * `variable.eslintExplicitGlobal` (`boolean | undefined`) ... If `true`, this global variable was defined by a `/* globals */` directive comment in the source code file. From 60de98a9c230cbeba7988e221c6b90954ce16170 Mon Sep 17 00:00:00 2001 From: Kevin Partington Date: Fri, 29 Mar 2019 09:33:44 +0900 Subject: [PATCH 12/13] Update lib/util/ast-utils.js Co-Authored-By: mysticatea --- lib/util/ast-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util/ast-utils.js b/lib/util/ast-utils.js index d00ee4f01c4..0f9ba290a9d 100644 --- a/lib/util/ast-utils.js +++ b/lib/util/ast-utils.js @@ -1364,7 +1364,7 @@ module.exports = { // Convert the index to loc. return sourceCode.getLocFromIndex( comment.range[0] + - 2 + // 2 is "/*".length + "/*".length + (match ? match.index + 1 : 0) ); } From 8a3d88ea3a61c42aed95f61d4d916d62fc2ef314 Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Fri, 29 Mar 2019 11:09:25 +0900 Subject: [PATCH 13/13] add column to some tests --- tests/lib/rules/no-redeclare.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/lib/rules/no-redeclare.js b/tests/lib/rules/no-redeclare.js index 06579fb96f9..15d8275d02b 100644 --- a/tests/lib/rules/no-redeclare.js +++ b/tests/lib/rules/no-redeclare.js @@ -310,7 +310,7 @@ ruleTester.run("no-redeclare", rule, { { code: "/*globals a */ /*globals a */", errors: [ - { message: "'a' is already defined.", type: "Block" } + { message: "'a' is already defined.", type: "Block", column: 26 } ] }, { @@ -318,9 +318,9 @@ ruleTester.run("no-redeclare", rule, { options: [{ builtinGlobals: true }], globals: { a: "writable" }, errors: [ - { message: "'a' is already defined as a built-in global variable.", type: "Block" }, - { message: "'a' is already defined as a built-in global variable.", type: "Block" }, - { message: "'a' is already defined as a built-in global variable.", type: "Identifier" } + { message: "'a' is already defined as a built-in global variable.", type: "Block", column: 11 }, + { message: "'a' is already defined as a built-in global variable.", type: "Block", column: 26 }, + { message: "'a' is already defined as a built-in global variable.", type: "Identifier", column: 35 } ] } ]