Skip to content

Commit

Permalink
feat!: Remove deprecated context methods (#17698)
Browse files Browse the repository at this point in the history
* feat!: Remove deprecated context methods

fixes #17520

* Fix broken tests

* Remove BASE_TRAVERSAL_CONTEXT

* Remove deprecated methods from RuleTester

* Removed more methods/properties

* Fix lint errors

* Update docs/src/extend/custom-rules.md

Co-authored-by: 唯然 <weiran.zsd@outlook.com>

* Update docs/src/extend/custom-rules.md

Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>

* Update docs/src/extend/custom-rules.md

Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>

* Update docs/src/extend/custom-rules.md

Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>

* Remove unneeded deprecated notices

* Restore parserServices tests

---------

Co-authored-by: 唯然 <weiran.zsd@outlook.com>
Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>
  • Loading branch information
3 people committed Dec 20, 2023
1 parent f71c328 commit ae78ff1
Show file tree
Hide file tree
Showing 8 changed files with 6,164 additions and 9,356 deletions.
31 changes: 13 additions & 18 deletions docs/src/extend/custom-rules.md
Expand Up @@ -131,28 +131,14 @@ The `context` object has the following properties:
* `sourceCode`: (`object`) A `SourceCode` object that you can use to work with the source that was passed to ESLint (see [Accessing the Source Code](#accessing-the-source-code)).
* `settings`: (`object`) The [shared settings](../use/configure/configuration-files#adding-shared-settings) from the configuration.
* `parserPath`: (`string`) The name of the `parser` from the configuration.
* `parserServices`: (**Deprecated:** Use `SourceCode#parserServices` instead.) Contains parser-provided services for rules. The default parser does not provide any services. However, if a rule is intended to be used with a custom parser, it could use `parserServices` to access anything provided by that parser. (For example, a TypeScript parser could provide the ability to get the computed type of a given node.)
* `parserOptions`: The parser options configured for this run (more details [here](../use/configure/language-options#specifying-parser-options)).

Additionally, the `context` object has the following methods:

* `getAncestors()`: (**Deprecated:** Use `SourceCode#getAncestors(node)` instead.) Returns an array of the ancestors of the currently-traversed node, starting at the root of the AST and continuing through the direct parent of the current node. This array does not include the currently-traversed node itself.
* `getCwd()`: (**Deprecated:** Use `context.cwd` instead.) Returns the `cwd` option passed to the [Linter](../integrate/nodejs-api#linter). It is a path to a directory that should be considered the current working directory.
* `getDeclaredVariables(node)`: (**Deprecated:** Use `SourceCode#getDeclaredVariables(node)` instead.) Returns a list of [variables](./scope-manager-interface#variable-interface) declared by the given node. This information can be used to track references to variables.
* If the node is a `VariableDeclaration`, all variables declared in the declaration are returned.
* If the node is a `VariableDeclarator`, all variables declared in the declarator are returned.
* If the node is a `FunctionDeclaration` or `FunctionExpression`, the variable for the function name is returned, in addition to variables for the function parameters.
* If the node is an `ArrowFunctionExpression`, variables for the parameters are returned.
* If the node is a `ClassDeclaration` or a `ClassExpression`, the variable for the class name is returned.
* If the node is a `CatchClause`, the variable for the exception is returned.
* If the node is an `ImportDeclaration`, variables for all of its specifiers are returned.
* If the node is an `ImportSpecifier`, `ImportDefaultSpecifier`, or `ImportNamespaceSpecifier`, the declared variable is returned.
* Otherwise, if the node does not declare any variables, an empty array is returned.
* `getFilename()`: (**Deprecated:** Use `context.filename` instead.) Returns the filename associated with the source.
* `getPhysicalFilename()`: (**Deprecated:** Use `context.physicalFilename` instead.) When linting a file, it returns the full path of the file on disk without any code block information. When linting text, it returns the value passed to `—stdin-filename` or `<text>` if not specified.
* `getScope()`: (**Deprecated:** Use `SourceCode#getScope(node)` instead.) Returns the [scope](./scope-manager-interface#scope-interface) of the currently-traversed node. This information can be used to track references to variables.
* `getSourceCode()`: (**Deprecated:** Use `context.sourceCode` instead.) Returns a `SourceCode` object that you can use to work with the source that was passed to ESLint (see [Accessing the Source Code](#accessing-the-source-code)).
* `markVariableAsUsed(name)`: (**Deprecated:** Use `SourceCode#markVariableAsUsed(name, node)` instead.) Marks a variable with the given name in the current scope as used. This affects the [no-unused-vars](../rules/no-unused-vars) rule. Returns `true` if a variable with the given name was found and marked as used, otherwise `false`.
* `report(descriptor)`. Reports a problem in the code (see the [dedicated section](#reporting-problems)).

**Note:** Earlier versions of ESLint supported additional methods on the `context` object. Those methods were removed in the new format and should not be relied upon.
Expand Down Expand Up @@ -551,6 +537,19 @@ Once you have an instance of `SourceCode`, you can use the following methods on
* `getLocFromIndex(index)`: Returns an object with `line` and `column` properties, corresponding to the location of the given source index. `line` is 1-based and `column` is 0-based.
* `getIndexFromLoc(loc)`: Returns the index of a given location in the source code, where `loc` is an object with a 1-based `line` key and a 0-based `column` key.
* `commentsExistBetween(nodeOrToken1, nodeOrToken2)`: Returns `true` if comments exist between two nodes.
* `getAncestors(node)`: Returns an array of the ancestors of the given node, starting at the root of the AST and continuing through the direct parent of the given node. This array does not include the given node itself.
* `getDeclaredVariables(node)`: Returns a list of [variables](./scope-manager-interface#variable-interface) declared by the given node. This information can be used to track references to variables.
* If the node is a `VariableDeclaration`, all variables declared in the declaration are returned.
* If the node is a `VariableDeclarator`, all variables declared in the declarator are returned.
* If the node is a `FunctionDeclaration` or `FunctionExpression`, the variable for the function name is returned, in addition to variables for the function parameters.
* If the node is an `ArrowFunctionExpression`, variables for the parameters are returned.
* If the node is a `ClassDeclaration` or a `ClassExpression`, the variable for the class name is returned.
* If the node is a `CatchClause`, the variable for the exception is returned.
* If the node is an `ImportDeclaration`, variables for all of its specifiers are returned.
* If the node is an `ImportSpecifier`, `ImportDefaultSpecifier`, or `ImportNamespaceSpecifier`, the declared variable is returned.
* Otherwise, if the node does not declare any variables, an empty array is returned.
* `getScope(node)`: Returns the [scope](./scope-manager-interface#scope-interface) of the given node. This information can be used to track references to variables.
* `markVariableAsUsed(name, refNode)`: Marks a variable with the given name in a scope indicated by the given reference node as used. This affects the [no-unused-vars](../rules/no-unused-vars) rule. Returns `true` if a variable with the given name was found and marked as used, otherwise `false`.

`skipOptions` is an object which has 3 properties; `skip`, `includeComments`, and `filter`. Default is `{skip: 0, includeComments: false, filter: null}`.

Expand Down Expand Up @@ -787,8 +786,6 @@ To learn more about JSON Schema, we recommend looking at some examples on the [J

The `SourceCode#getScope(node)` method returns the scope of the given node. It is a useful method for finding information about the variables in a given scope and how they are used in other scopes.

**Deprecated:** The `context.getScope()` is deprecated; make sure to use `SourceCode#getScope(node)` instead.

#### Scope types

The following table contains a list of AST node types and the scope type that they correspond to. For more information about the scope types, refer to the [`Scope` object documentation](./scope-manager-interface#scope-interface).
Expand Down Expand Up @@ -836,8 +833,6 @@ For examples of using `SourceCode#getScope()` to track variables, refer to the s

### Marking Variables as Used

**Deprecated:** The `context.markVariableAsUsed()` method is deprecated in favor of `sourceCode.markVariableAsUsed()`.

Certain ESLint rules, such as [`no-unused-vars`](../rules/no-unused-vars), check to see if a variable has been used. ESLint itself only knows about the standard rules of variable access and so custom ways of accessing variables may not register as "used".

To help with this, you can use the `sourceCode.markVariableAsUsed()` method. This method takes two arguments: the name of the variable to mark as used and an option reference node indicating the scope in which you are working. Here's an example:
Expand Down
77 changes: 16 additions & 61 deletions lib/linter/linter.js
Expand Up @@ -899,43 +899,6 @@ function createRuleListeners(rule, ruleContext) {
}
}

// methods that exist on SourceCode object
const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
getSource: "getText",
getSourceLines: "getLines",
getAllComments: "getAllComments",
getNodeByRangeIndex: "getNodeByRangeIndex",
getComments: "getComments",
getCommentsBefore: "getCommentsBefore",
getCommentsAfter: "getCommentsAfter",
getCommentsInside: "getCommentsInside",
getJSDocComment: "getJSDocComment",
getFirstToken: "getFirstToken",
getFirstTokens: "getFirstTokens",
getLastToken: "getLastToken",
getLastTokens: "getLastTokens",
getTokenAfter: "getTokenAfter",
getTokenBefore: "getTokenBefore",
getTokenByRangeStart: "getTokenByRangeStart",
getTokens: "getTokens",
getTokensAfter: "getTokensAfter",
getTokensBefore: "getTokensBefore",
getTokensBetween: "getTokensBetween"
};


const BASE_TRAVERSAL_CONTEXT = Object.freeze(
Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).reduce(
(contextInfo, methodName) =>
Object.assign(contextInfo, {
[methodName](...args) {
return this.sourceCode[DEPRECATED_SOURCECODE_PASSTHROUGHS[methodName]](...args);
}
}),
{}
)
);

/**
* Runs the given rules on the given SourceCode object
* @param {SourceCode} sourceCode A SourceCode object for the given text
Expand Down Expand Up @@ -972,30 +935,22 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
* properties once for each rule.
*/
const sharedTraversalContext = Object.freeze(
Object.assign(
Object.create(BASE_TRAVERSAL_CONTEXT),
{
getAncestors: () => sourceCode.getAncestors(currentNode),
getDeclaredVariables: node => sourceCode.getDeclaredVariables(node),
getCwd: () => cwd,
cwd,
getFilename: () => filename,
filename,
getPhysicalFilename: () => physicalFilename || filename,
physicalFilename: physicalFilename || filename,
getScope: () => sourceCode.getScope(currentNode),
getSourceCode: () => sourceCode,
sourceCode,
markVariableAsUsed: name => sourceCode.markVariableAsUsed(name, currentNode),
parserOptions: {
...languageOptions.parserOptions
},
parserPath: parserName,
languageOptions,
parserServices: sourceCode.parserServices,
settings
}
)
{
getCwd: () => cwd,
cwd,
getFilename: () => filename,
filename,
getPhysicalFilename: () => physicalFilename || filename,
physicalFilename: physicalFilename || filename,
getSourceCode: () => sourceCode,
sourceCode,
parserOptions: {
...languageOptions.parserOptions
},
parserPath: parserName,
languageOptions,
settings
}
);

const lintingProblems = [];
Expand Down
94 changes: 1 addition & 93 deletions lib/rule-tester/rule-tester.js
Expand Up @@ -171,35 +171,6 @@ const forbiddenMethods = [

const hasOwnProperty = Function.call.bind(Object.hasOwnProperty);

const DEPRECATED_SOURCECODE_PASSTHROUGHS = {
getSource: "getText",
getSourceLines: "getLines",
getAllComments: "getAllComments",
getNodeByRangeIndex: "getNodeByRangeIndex",

// getComments: "getComments", -- already handled by a separate error
getCommentsBefore: "getCommentsBefore",
getCommentsAfter: "getCommentsAfter",
getCommentsInside: "getCommentsInside",
getJSDocComment: "getJSDocComment",
getFirstToken: "getFirstToken",
getFirstTokens: "getFirstTokens",
getLastToken: "getLastToken",
getLastTokens: "getLastTokens",
getTokenAfter: "getTokenAfter",
getTokenBefore: "getTokenBefore",
getTokenByRangeStart: "getTokenByRangeStart",
getTokens: "getTokens",
getTokensAfter: "getTokensAfter",
getTokensBefore: "getTokensBefore",
getTokensBetween: "getTokensBetween",

getScope: "getScope",
getAncestors: "getAncestors",
getDeclaredVariables: "getDeclaredVariables",
markVariableAsUsed: "markVariableAsUsed"
};

/**
* Clones a given value deeply.
* Note: This ignores `parent` property.
Expand Down Expand Up @@ -384,22 +355,6 @@ function emitMissingSchemaWarning(ruleName) {
}
}

/**
* Emit a deprecation warning if a rule uses a deprecated `context` method.
* @param {string} ruleName Name of the rule.
* @param {string} methodName The name of the method on `context` that was used.
* @returns {void}
*/
function emitDeprecatedContextMethodWarning(ruleName, methodName) {
if (!emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`]) {
emitDeprecatedContextMethodWarning[`warned-${ruleName}-${methodName}`] = true;
process.emitWarning(
`"${ruleName}" rule is using \`context.${methodName}()\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.${DEPRECATED_SOURCECODE_PASSTHROUGHS[methodName]}()\` instead.`,
"DeprecationWarning"
);
}
}

/**
* Emit a deprecation warning if rule uses CodePath#currentSegments.
* @param {string} ruleName Name of the rule.
Expand All @@ -415,22 +370,6 @@ function emitCodePathCurrentSegmentsWarning(ruleName) {
}
}

/**
* Emit a deprecation warning if `context.parserServices` is used.
* @param {string} ruleName Name of the rule.
* @returns {void}
*/
function emitParserServicesWarning(ruleName) {
if (!emitParserServicesWarning[`warned-${ruleName}`]) {
emitParserServicesWarning[`warned-${ruleName}`] = true;
process.emitWarning(
`"${ruleName}" rule is using \`context.parserServices\`, which is deprecated and will be removed in ESLint v9. Please use \`sourceCode.parserServices\` instead.`,
"DeprecationWarning"
);
}
}


//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------
Expand Down Expand Up @@ -662,38 +601,7 @@ class RuleTester {
freezeDeeply(context.settings);
freezeDeeply(context.parserOptions);

// wrap all deprecated methods
const newContext = Object.create(
context,
Object.fromEntries(Object.keys(DEPRECATED_SOURCECODE_PASSTHROUGHS).map(methodName => [
methodName,
{
value(...args) {

// emit deprecation warning
emitDeprecatedContextMethodWarning(ruleName, methodName);

// call the original method
return context[methodName].call(this, ...args);
},
enumerable: true
}
]))
);

// emit warning about context.parserServices
const parserServices = context.parserServices;

Object.defineProperty(newContext, "parserServices", {
get() {
emitParserServicesWarning(ruleName);
return parserServices;
}
});

Object.freeze(newContext);

return (typeof rule === "function" ? rule : rule.create)(newContext);
return (typeof rule === "function" ? rule : rule.create)(context);
}
}));

Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -120,7 +120,7 @@
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-eslint-plugin": "^5.2.1",
"eslint-plugin-internal-rules": "file:tools/internal-rules",
"eslint-plugin-jsdoc": "^46.2.5",
"eslint-plugin-jsdoc": "^46.9.0",
"eslint-plugin-n": "^16.4.0",
"eslint-plugin-unicorn": "^49.0.0",
"eslint-release": "^3.2.0",
Expand Down

0 comments on commit ae78ff1

Please sign in to comment.