Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/src/extend/custom-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ Once you have an instance of `SourceCode`, you can use the following methods on
- `getCommentsAfter(nodeOrToken)`: Returns an array of comment tokens that occur directly after the given node or token (see the [dedicated section](#accessing-comments)).
- `getCommentsInside(node)`: Returns an array of all comment tokens inside a given node (see the [dedicated section](#accessing-comments)).
- `isSpaceBetween(nodeOrToken, nodeOrToken)`: Returns true if there is a whitespace character between the two tokens or, if given a node, the last token of the first node and the first token of the second node.
- `isGlobalReference(node)`: Returns true if the given identifier node is a reference to a global variable.
- `getFirstToken(node, skipOptions)`: Returns the first token representing the given node.
- `getFirstTokens(node, countOptions)`: Returns the first `count` tokens representing the given node.
- `getLastToken(node, skipOptions)`: Returns the last token representing the given node.
Expand Down
31 changes: 31 additions & 0 deletions lib/languages/js/source-code/source-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,37 @@ class SourceCode extends TokenStore {
return ancestorsStartingAtParent.reverse();
}

/**
* Determines whether the given identifier node is a reference to a global variable.
* @param {ASTNode} node `Identifier` node to check.
* @returns {boolean} True if the identifier is a reference to a global variable.
*/
isGlobalReference(node) {
if (!node) {
throw new TypeError("Missing required argument: node.");
}

if (node.type !== "Identifier") {
return false;
}

const variable = this.scopeManager.scopes[0].set.get(node.name);

if (!variable || variable.defs.length > 0) {
return false;
}

const references = variable.references;

for (const reference of references) {
if (reference.identifier === node) {
return true;
}
}

return false;
}

/**
* Returns the location of the given node or token.
* @param {ASTNode|Token} nodeOrToken The node or token to get the location of.
Expand Down
39 changes: 4 additions & 35 deletions lib/rules/no-promise-executor-return.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
// Requirements
//------------------------------------------------------------------------------

const { findVariable } = require("@eslint-community/eslint-utils");
const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
Expand All @@ -21,51 +20,21 @@ const functionTypesToCheck = new Set([
"FunctionExpression",
]);

/**
* Determines whether the given identifier node is a reference to a global variable.
* @param {ASTNode} node `Identifier` node to check.
* @param {Scope} scope Scope to which the node belongs.
* @returns {boolean} True if the identifier is a reference to a global variable.
*/
function isGlobalReference(node, scope) {
const variable = findVariable(scope, node);

return (
variable !== null &&
variable.scope.type === "global" &&
variable.defs.length === 0
);
}

/**
* Finds function's outer scope.
* @param {Scope} scope Function's own scope.
* @returns {Scope} Function's outer scope.
*/
function getOuterScope(scope) {
const upper = scope.upper;

if (upper.type === "function-expression-name") {
return upper.upper;
}
return upper;
}

/**
* Determines whether the given function node is used as a Promise executor.
* @param {ASTNode} node The node to check.
* @param {Scope} scope Function's own scope.
* @param {SourceCode} sourceCode Source code to which the node belongs.
* @returns {boolean} `true` if the node is a Promise executor.
*/
function isPromiseExecutor(node, scope) {
function isPromiseExecutor(node, sourceCode) {
const parent = node.parent;

return (
parent.type === "NewExpression" &&
parent.arguments[0] === node &&
parent.callee.type === "Identifier" &&
parent.callee.name === "Promise" &&
isGlobalReference(parent.callee, getOuterScope(scope))
sourceCode.isGlobalReference(parent.callee)
);
}

Expand Down Expand Up @@ -203,7 +172,7 @@ module.exports = {
upper: funcInfo,
shouldCheck:
functionTypesToCheck.has(node.type) &&
isPromiseExecutor(node, sourceCode.getScope(node)),
isPromiseExecutor(node, sourceCode),
};

if (
Expand Down
61 changes: 13 additions & 48 deletions lib/rules/no-setter-return.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,24 @@
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");
const { findVariable } = require("@eslint-community/eslint-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
* Determines whether the given identifier node is a reference to a global variable.
* @param {ASTNode} node `Identifier` node to check.
* @param {Scope} scope Scope to which the node belongs.
* @returns {boolean} True if the identifier is a reference to a global variable.
*/
function isGlobalReference(node, scope) {
const variable = findVariable(scope, node);

return (
variable !== null &&
variable.scope.type === "global" &&
variable.defs.length === 0
);
}

/**
* Determines whether the given node is an argument of the specified global method call, at the given `index` position.
* E.g., for given `index === 1`, this function checks for `objectName.methodName(foo, node)`, where objectName is a global variable.
* @param {ASTNode} node The node to check.
* @param {Scope} scope Scope to which the node belongs.
* @param {SourceCode} sourceCode Source code to which the node belongs.
* @param {string} objectName Name of the global object.
* @param {string} methodName Name of the method.
* @param {number} index The given position.
* @returns {boolean} `true` if the node is argument at the given position.
*/
function isArgumentOfGlobalMethodCall(
node,
scope,
sourceCode,
objectName,
methodName,
index,
Expand All @@ -59,31 +42,30 @@ function isArgumentOfGlobalMethodCall(
objectName,
methodName,
) &&
isGlobalReference(
sourceCode.isGlobalReference(
astUtils.skipChainExpression(callNode.callee).object,
scope,
)
);
}

/**
* Determines whether the given node is used as a property descriptor.
* @param {ASTNode} node The node to check.
* @param {Scope} scope Scope to which the node belongs.
* @param {SourceCode} sourceCode Source code to which the node belongs.
* @returns {boolean} `true` if the node is a property descriptor.
*/
function isPropertyDescriptor(node, scope) {
function isPropertyDescriptor(node, sourceCode) {
if (
isArgumentOfGlobalMethodCall(
node,
scope,
sourceCode,
"Object",
"defineProperty",
2,
) ||
isArgumentOfGlobalMethodCall(
node,
scope,
sourceCode,
"Reflect",
"defineProperty",
2,
Expand All @@ -101,14 +83,14 @@ function isPropertyDescriptor(node, scope) {
grandparent.type === "ObjectExpression" &&
(isArgumentOfGlobalMethodCall(
grandparent,
scope,
sourceCode,
"Object",
"create",
1,
) ||
isArgumentOfGlobalMethodCall(
grandparent,
scope,
sourceCode,
"Object",
"defineProperties",
1,
Expand All @@ -124,10 +106,10 @@ function isPropertyDescriptor(node, scope) {
/**
* Determines whether the given function node is used as a setter function.
* @param {ASTNode} node The node to check.
* @param {Scope} scope Scope to which the node belongs.
* @param {SourceCode} sourceCode Source code to which the node belongs.
* @returns {boolean} `true` if the node is a setter.
*/
function isSetter(node, scope) {
function isSetter(node, sourceCode) {
const parent = node.parent;

if (
Expand All @@ -144,7 +126,7 @@ function isSetter(node, scope) {
parent.value === node &&
astUtils.getStaticPropertyName(parent) === "set" &&
parent.parent.type === "ObjectExpression" &&
isPropertyDescriptor(parent.parent, scope)
isPropertyDescriptor(parent.parent, sourceCode)
) {
// Setter in a property descriptor
return true;
Expand All @@ -153,21 +135,6 @@ function isSetter(node, scope) {
return false;
}

/**
* Finds function's outer scope.
* @param {Scope} scope Function's own scope.
* @returns {Scope} Function's outer scope.
*/
function getOuterScope(scope) {
const upper = scope.upper;

if (upper.type === "function-expression-name") {
return upper.upper;
}

return upper;
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
Expand Down Expand Up @@ -200,11 +167,9 @@ module.exports = {
* @returns {void}
*/
function enterFunction(node) {
const outerScope = getOuterScope(sourceCode.getScope(node));

funcInfo = {
upper: funcInfo,
isSetter: isSetter(node, outerScope),
isSetter: isSetter(node, sourceCode),
};
}

Expand Down
19 changes: 1 addition & 18 deletions lib/rules/prefer-regex-literals.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const {
CALL,
CONSTRUCT,
ReferenceTracker,
findVariable,
} = require("@eslint-community/eslint-utils");
const {
RegExpValidator,
Expand Down Expand Up @@ -167,22 +166,6 @@ module.exports = {
const [{ disallowRedundantWrapping }] = context.options;
const sourceCode = context.sourceCode;

/**
* Determines whether the given identifier node is a reference to a global variable.
* @param {ASTNode} node `Identifier` node to check.
* @returns {boolean} True if the identifier is a reference to a global variable.
*/
function isGlobalReference(node) {
const scope = sourceCode.getScope(node);
const variable = findVariable(scope, node);

return (
variable !== null &&
variable.scope.type === "global" &&
variable.defs.length === 0
);
}

/**
* Determines whether the given node is a String.raw`` tagged template expression
* with a static template literal.
Expand All @@ -193,7 +176,7 @@ module.exports = {
return (
node.type === "TaggedTemplateExpression" &&
astUtils.isSpecificMemberAccess(node.tag, "String", "raw") &&
isGlobalReference(
sourceCode.isGlobalReference(
astUtils.skipChainExpression(node.tag).object,
) &&
astUtils.isStaticTemplateLiteral(node.quasi)
Expand Down
Loading
Loading