Repository: eslint-community/eslint-plugin-eslint-plugin
Pull request #400 (2023-12-08 09:11:07)

feat: preparing for eslint v9
refs: https://eslint.org/blog/2023/09/preparing-custom-rules-eslint-v9/


Patch:
diff --git a/lib/rules/fixer-return.js b/lib/rules/fixer-return.js
index 05e6867..ac231d8 100644
--- a/lib/rules/fixer-return.js
+++ b/lib/rules/fixer-return.js
@@ -76,12 +76,13 @@ module.exports = {
      * @returns {boolean}
      */
     function isFix(node) {
+      const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: use context.sourceCode when dropping eslint < v9
       if (node.type === 'ArrayExpression' && node.elements.length === 0) {
         // An empty array is not a fix.
         return false;
       }
-
-      const staticValue = getStaticValue(node, context.getScope());
+      const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < 9.0.0
+      const staticValue = getStaticValue(node, scope);
       if (!staticValue) {
         // If we can't find a static value, assume it's a real fix value.
         return true;
@@ -98,7 +99,7 @@ module.exports = {
 
     return {
       Program(ast) {
-        const sourceCode = context.getSourceCode();
+        const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
         contextIdentifiers = utils.getContextIdentifiers(
           sourceCode.scopeManager,
           ast
@@ -148,7 +149,8 @@ module.exports = {
       // Ensure the current (arrow) fixer function returned a fix.
       'ArrowFunctionExpression:exit'(node) {
         if (funcInfo.shouldCheck) {
-          const loc = context.getSourceCode().getTokenBefore(node.body).loc; // Show violation on arrow (=>).
+          const sourceCode = context.sourceCode || context.getSourceCode();
+          const loc = sourceCode.getTokenBefore(node.body).loc; // Show violation on arrow (=>).
           if (node.expression) {
             // When the return is implied (no curly braces around the body), we have to check the single body node directly.
             if (!isFix(node.body)) {
diff --git a/lib/rules/meta-property-ordering.js b/lib/rules/meta-property-ordering.js
index 8c19c12..d07215d 100644
--- a/lib/rules/meta-property-ordering.js
+++ b/lib/rules/meta-property-ordering.js
@@ -34,7 +34,7 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const ruleInfo = getRuleInfo(sourceCode);
     if (!ruleInfo) {
       return {};
diff --git a/lib/rules/no-deprecated-context-methods.js b/lib/rules/no-deprecated-context-methods.js
index 97ced0e..3d68edb 100644
--- a/lib/rules/no-deprecated-context-methods.js
+++ b/lib/rules/no-deprecated-context-methods.js
@@ -54,7 +54,7 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
 
     // ----------------------------------------------------------------------
     // Public
diff --git a/lib/rules/no-deprecated-report-api.js b/lib/rules/no-deprecated-report-api.js
index d6c7b37..59ff825 100644
--- a/lib/rules/no-deprecated-report-api.js
+++ b/lib/rules/no-deprecated-report-api.js
@@ -30,7 +30,7 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     let contextIdentifiers;
 
     // ----------------------------------------------------------------------
@@ -60,7 +60,7 @@ module.exports = {
             fix(fixer) {
               const openingParen = sourceCode.getTokenBefore(node.arguments[0]);
               const closingParen = sourceCode.getLastToken(node);
-              const reportInfo = utils.getReportInfo(node.arguments, context);
+              const reportInfo = utils.getReportInfo(node, context);
 
               if (!reportInfo) {
                 return null;
diff --git a/lib/rules/no-missing-message-ids.js b/lib/rules/no-missing-message-ids.js
index dec2f17..73d12dd 100644
--- a/lib/rules/no-missing-message-ids.js
+++ b/lib/rules/no-missing-message-ids.js
@@ -26,7 +26,7 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const { scopeManager } = sourceCode;
     const ruleInfo = utils.getRuleInfo(sourceCode);
     if (!ruleInfo) {
@@ -48,6 +48,7 @@ module.exports = {
       },
 
       CallExpression(node) {
+        const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < 9.0.0
         // Check for messageId properties used in known calls to context.report();
         if (
           node.callee.type === 'MemberExpression' &&
@@ -55,7 +56,7 @@ module.exports = {
           node.callee.property.type === 'Identifier' &&
           node.callee.property.name === 'report'
         ) {
-          const reportInfo = utils.getReportInfo(node.arguments, context);
+          const reportInfo = utils.getReportInfo(node, context);
           if (!reportInfo) {
             return;
           }
@@ -80,7 +81,7 @@ module.exports = {
                   val.value,
                   ruleInfo,
                   scopeManager,
-                  context.getScope()
+                  scope
                 )
               )
                 // Couldn't find this messageId in `meta.messages`.
diff --git a/lib/rules/no-missing-placeholders.js b/lib/rules/no-missing-placeholders.js
index a70fbd9..f505d4f 100644
--- a/lib/rules/no-missing-placeholders.js
+++ b/lib/rules/no-missing-placeholders.js
@@ -31,7 +31,7 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const { scopeManager } = sourceCode;
 
     let contextIdentifiers;
@@ -48,13 +48,14 @@ module.exports = {
         contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast);
       },
       CallExpression(node) {
+        const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < 9.0.0
         if (
           node.callee.type === 'MemberExpression' &&
           contextIdentifiers.has(node.callee.object) &&
           node.callee.property.type === 'Identifier' &&
           node.callee.property.name === 'report'
         ) {
-          const reportInfo = utils.getReportInfo(node.arguments, context);
+          const reportInfo = utils.getReportInfo(node, context);
           if (!reportInfo) {
             return;
           }
@@ -75,7 +76,7 @@ module.exports = {
                   obj.messageId.value,
                   ruleInfo,
                   scopeManager,
-                  context.getScope()
+                  scope
                 );
                 if (correspondingMessage) {
                   obj.message = correspondingMessage.value;
@@ -89,10 +90,7 @@ module.exports = {
             messageId,
             data,
           } of reportMessagesAndDataArray.filter((obj) => obj.message)) {
-            const messageStaticValue = getStaticValue(
-              message,
-              context.getScope()
-            );
+            const messageStaticValue = getStaticValue(message, scope);
             if (
               ((message.type === 'Literal' &&
                 typeof message.value === 'string') ||
diff --git a/lib/rules/no-unused-message-ids.js b/lib/rules/no-unused-message-ids.js
index 2bd1b45..86840d1 100644
--- a/lib/rules/no-unused-message-ids.js
+++ b/lib/rules/no-unused-message-ids.js
@@ -24,7 +24,7 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const { scopeManager } = sourceCode;
     const ruleInfo = utils.getRuleInfo(sourceCode);
     if (!ruleInfo) {
@@ -47,7 +47,7 @@ module.exports = {
         contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast);
       },
 
-      'Program:exit'() {
+      'Program:exit'(ast) {
         if (hasSeenUnknownMessageId || !hasSeenViolationReport) {
           /*
           Bail out when the rule is likely to have false positives.
@@ -57,9 +57,10 @@ module.exports = {
           return;
         }
 
+        const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < 9.0.0
+
         const messageIdNodesUnused = messageIdNodes.filter(
-          (node) =>
-            !messageIdsUsed.has(utils.getKeyName(node, context.getScope()))
+          (node) => !messageIdsUsed.has(utils.getKeyName(node, scope))
         );
 
         // Report any messageIds that were never used.
@@ -68,7 +69,7 @@ module.exports = {
             node: messageIdNode,
             messageId: 'unusedMessage',
             data: {
-              messageId: utils.getKeyName(messageIdNode, context.getScope()),
+              messageId: utils.getKeyName(messageIdNode, scope),
             },
           });
         }
@@ -82,7 +83,7 @@ module.exports = {
           node.callee.property.type === 'Identifier' &&
           node.callee.property.name === 'report'
         ) {
-          const reportInfo = utils.getReportInfo(node.arguments, context);
+          const reportInfo = utils.getReportInfo(node, context);
           if (!reportInfo) {
             return;
           }
diff --git a/lib/rules/no-unused-placeholders.js b/lib/rules/no-unused-placeholders.js
index 51defef..edace9c 100644
--- a/lib/rules/no-unused-placeholders.js
+++ b/lib/rules/no-unused-placeholders.js
@@ -31,7 +31,7 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const { scopeManager } = sourceCode;
 
     let contextIdentifiers;
@@ -47,13 +47,14 @@ module.exports = {
         contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast);
       },
       CallExpression(node) {
+        const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < 9.0.0
         if (
           node.callee.type === 'MemberExpression' &&
           contextIdentifiers.has(node.callee.object) &&
           node.callee.property.type === 'Identifier' &&
           node.callee.property.name === 'report'
         ) {
-          const reportInfo = utils.getReportInfo(node.arguments, context);
+          const reportInfo = utils.getReportInfo(node, context);
           if (!reportInfo) {
             return;
           }
@@ -74,7 +75,7 @@ module.exports = {
                   obj.messageId.value,
                   ruleInfo,
                   scopeManager,
-                  context.getScope()
+                  scope
                 );
                 if (correspondingMessage) {
                   obj.message = correspondingMessage.value;
@@ -86,10 +87,7 @@ module.exports = {
           for (const { message, data } of reportMessagesAndDataArray.filter(
             (obj) => obj.message
           )) {
-            const messageStaticValue = getStaticValue(
-              message,
-              context.getScope()
-            );
+            const messageStaticValue = getStaticValue(message, scope);
             if (
               ((message.type === 'Literal' &&
                 typeof message.value === 'string') ||
diff --git a/lib/rules/no-useless-token-range.js b/lib/rules/no-useless-token-range.js
index e62d731..ea48819 100644
--- a/lib/rules/no-useless-token-range.js
+++ b/lib/rules/no-useless-token-range.js
@@ -30,7 +30,7 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
 
     // ----------------------------------------------------------------------
     // Helpers
diff --git a/lib/rules/prefer-message-ids.js b/lib/rules/prefer-message-ids.js
index 539d710..48bf348 100644
--- a/lib/rules/prefer-message-ids.js
+++ b/lib/rules/prefer-message-ids.js
@@ -29,7 +29,7 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const ruleInfo = utils.getRuleInfo(sourceCode);
     if (!ruleInfo) {
       return {};
@@ -43,6 +43,7 @@ module.exports = {
 
     return {
       Program(ast) {
+        const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
         contextIdentifiers = utils.getContextIdentifiers(
           sourceCode.scopeManager,
           ast
@@ -64,10 +65,7 @@ module.exports = {
           return;
         }
 
-        const staticValue = getStaticValue(
-          messagesNode.value,
-          context.getScope()
-        );
+        const staticValue = getStaticValue(messagesNode.value, scope);
         if (!staticValue) {
           return;
         }
@@ -90,7 +88,7 @@ module.exports = {
           node.callee.property.type === 'Identifier' &&
           node.callee.property.name === 'report'
         ) {
-          const reportInfo = utils.getReportInfo(node.arguments, context);
+          const reportInfo = utils.getReportInfo(node, context);
           if (!reportInfo) {
             return;
           }
diff --git a/lib/rules/prefer-object-rule.js b/lib/rules/prefer-object-rule.js
index 46bc4ce..4cb11fc 100644
--- a/lib/rules/prefer-object-rule.js
+++ b/lib/rules/prefer-object-rule.js
@@ -32,7 +32,7 @@ module.exports = {
     // Public
     // ----------------------------------------------------------------------
 
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const ruleInfo = utils.getRuleInfo(sourceCode);
     if (!ruleInfo) {
       return {};
diff --git a/lib/rules/prefer-output-null.js b/lib/rules/prefer-output-null.js
index 4403b7e..d58e430 100644
--- a/lib/rules/prefer-output-null.js
+++ b/lib/rules/prefer-output-null.js
@@ -35,7 +35,7 @@ module.exports = {
     // Public
     // ----------------------------------------------------------------------
 
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
 
     return {
       Program(ast) {
diff --git a/lib/rules/prefer-placeholders.js b/lib/rules/prefer-placeholders.js
index cc29d4b..bd3457c 100644
--- a/lib/rules/prefer-placeholders.js
+++ b/lib/rules/prefer-placeholders.js
@@ -33,7 +33,7 @@ module.exports = {
   create(context) {
     let contextIdentifiers;
 
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const { scopeManager } = sourceCode;
 
     // ----------------------------------------------------------------------
@@ -51,7 +51,7 @@ module.exports = {
           node.callee.property.type === 'Identifier' &&
           node.callee.property.name === 'report'
         ) {
-          const reportInfo = utils.getReportInfo(node.arguments, context);
+          const reportInfo = utils.getReportInfo(node, context);
 
           if (!reportInfo) {
             return;
diff --git a/lib/rules/prefer-replace-text.js b/lib/rules/prefer-replace-text.js
index c83beea..a9517ed 100644
--- a/lib/rules/prefer-replace-text.js
+++ b/lib/rules/prefer-replace-text.js
@@ -30,7 +30,7 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     let funcInfo = {
       upper: null,
       codePath: null,
diff --git a/lib/rules/report-message-format.js b/lib/rules/report-message-format.js
index 770524c..d22d9cf 100644
--- a/lib/rules/report-message-format.js
+++ b/lib/rules/report-message-format.js
@@ -38,8 +38,8 @@ module.exports = {
      * @param {ASTNode} message The message AST node
      * @returns {void}
      */
-    function processMessageNode(message) {
-      const staticValue = getStaticValue(message, context.getScope());
+    function processMessageNode(message, scope) {
+      const staticValue = getStaticValue(message, scope);
       if (
         (message.type === 'Literal' &&
           typeof message.value === 'string' &&
@@ -57,7 +57,7 @@ module.exports = {
       }
     }
 
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const ruleInfo = utils.getRuleInfo(sourceCode);
     if (!ruleInfo) {
       return {};
@@ -69,6 +69,7 @@ module.exports = {
 
     return {
       Program(ast) {
+        const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
         contextIdentifiers = utils.getContextIdentifiers(
           sourceCode.scopeManager,
           ast
@@ -93,21 +94,22 @@ module.exports = {
         messagesObject.value.properties
           .filter((prop) => prop.type === 'Property')
           .map((prop) => prop.value)
-          .forEach(processMessageNode);
+          .forEach((it) => processMessageNode(it, scope));
       },
       CallExpression(node) {
+        const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
         if (
           node.callee.type === 'MemberExpression' &&
           contextIdentifiers.has(node.callee.object) &&
           node.callee.property.type === 'Identifier' &&
           node.callee.property.name === 'report'
         ) {
-          const reportInfo = utils.getReportInfo(node.arguments, context);
+          const reportInfo = utils.getReportInfo(node, context);
           const message = reportInfo && reportInfo.message;
           const suggest = reportInfo && reportInfo.suggest;
 
           if (message) {
-            processMessageNode(message);
+            processMessageNode(message, scope);
           }
 
           if (suggest && suggest.type === 'ArrayExpression') {
@@ -122,7 +124,7 @@ module.exports = {
                   prop.key.name === 'message'
               )
               .map((prop) => prop.value)
-              .forEach(processMessageNode);
+              .forEach((it) => processMessageNode(it, scope));
           }
         }
       },
diff --git a/lib/rules/require-meta-docs-description.js b/lib/rules/require-meta-docs-description.js
index 85a374e..3afd6d8 100644
--- a/lib/rules/require-meta-docs-description.js
+++ b/lib/rules/require-meta-docs-description.js
@@ -45,14 +45,15 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const ruleInfo = utils.getRuleInfo(sourceCode);
     if (!ruleInfo) {
       return {};
     }
 
     return {
-      Program() {
+      Program(ast) {
+        const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
         const { scopeManager } = sourceCode;
 
         const pattern =
@@ -80,10 +81,7 @@ module.exports = {
           return;
         }
 
-        const staticValue = getStaticValue(
-          descriptionNode.value,
-          context.getScope()
-        );
+        const staticValue = getStaticValue(descriptionNode.value, scope);
         if (!staticValue) {
           // Ignore non-static values since we can't determine what they look like.
           return;
diff --git a/lib/rules/require-meta-docs-url.js b/lib/rules/require-meta-docs-url.js
index 862e6dd..d52f198 100644
--- a/lib/rules/require-meta-docs-url.js
+++ b/lib/rules/require-meta-docs-url.js
@@ -54,7 +54,7 @@ module.exports = {
    */
   create(context) {
     const options = context.options[0] || {};
-    const filename = context.getFilename();
+    const filename = context.filename || context.getFilename(); // TODO: just use context.filename when dropping eslint < v9
     const ruleName =
       filename === '<input>'
         ? undefined
@@ -76,14 +76,15 @@ module.exports = {
       );
     }
 
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const ruleInfo = util.getRuleInfo(sourceCode);
     if (!ruleInfo) {
       return {};
     }
 
     return {
-      Program() {
+      Program(ast) {
+        const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
         const { scopeManager } = sourceCode;
 
         const metaNode = ruleInfo.meta;
@@ -98,7 +99,7 @@ module.exports = {
           .find((p) => p.type === 'Property' && util.getKeyName(p) === 'url');
 
         const staticValue = urlPropNode
-          ? getStaticValue(urlPropNode.value, context.getScope())
+          ? getStaticValue(urlPropNode.value, scope)
           : undefined;
         if (urlPropNode && !staticValue) {
           // Ignore non-static values since we can't determine what they look like.
diff --git a/lib/rules/require-meta-fixable.js b/lib/rules/require-meta-fixable.js
index be3edf1..3b9ec68 100644
--- a/lib/rules/require-meta-fixable.js
+++ b/lib/rules/require-meta-fixable.js
@@ -49,7 +49,7 @@ module.exports = {
     const catchNoFixerButFixableProperty =
       context.options[0] && context.options[0].catchNoFixerButFixableProperty;
 
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const { scopeManager } = sourceCode;
     const ruleInfo = utils.getRuleInfo(sourceCode);
     let contextIdentifiers;
@@ -78,7 +78,8 @@ module.exports = {
           usesFixFunctions = true;
         }
       },
-      'Program:exit'() {
+      'Program:exit'(ast) {
+        const scope = sourceCode.getScope?.(ast) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
         const metaFixableProp =
           ruleInfo &&
           utils
@@ -86,10 +87,7 @@ module.exports = {
             .find((prop) => utils.getKeyName(prop) === 'fixable');
 
         if (metaFixableProp) {
-          const staticValue = getStaticValue(
-            metaFixableProp.value,
-            context.getScope()
-          );
+          const staticValue = getStaticValue(metaFixableProp.value, scope);
           if (!staticValue) {
             // Ignore non-static values since we can't determine what they look like.
             return;
diff --git a/lib/rules/require-meta-has-suggestions.js b/lib/rules/require-meta-has-suggestions.js
index 0a8f528..3c9a005 100644
--- a/lib/rules/require-meta-has-suggestions.js
+++ b/lib/rules/require-meta-has-suggestions.js
@@ -29,7 +29,7 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const { scopeManager } = sourceCode;
     const ruleInfo = utils.getRuleInfo(sourceCode);
     if (!ruleInfo) {
@@ -44,7 +44,8 @@ module.exports = {
      * @returns {boolean} whether this property should be considered to contain suggestions
      */
     function doesPropertyContainSuggestions(node) {
-      const staticValue = getStaticValue(node.value, context.getScope());
+      const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
+      const staticValue = getStaticValue(node.value, scope);
       if (
         !staticValue ||
         (Array.isArray(staticValue.value) && staticValue.value.length > 0) ||
diff --git a/lib/rules/require-meta-schema.js b/lib/rules/require-meta-schema.js
index 26a1cef..30d2410 100644
--- a/lib/rules/require-meta-schema.js
+++ b/lib/rules/require-meta-schema.js
@@ -43,7 +43,7 @@ module.exports = {
   },
 
   create(context) {
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const { scopeManager } = sourceCode;
     const ruleInfo = utils.getRuleInfo(sourceCode);
     if (!ruleInfo) {
diff --git a/lib/rules/require-meta-type.js b/lib/rules/require-meta-type.js
index 9a56066..1b3a14c 100644
--- a/lib/rules/require-meta-type.js
+++ b/lib/rules/require-meta-type.js
@@ -38,14 +38,15 @@ module.exports = {
     // Public
     // ----------------------------------------------------------------------
 
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const ruleInfo = utils.getRuleInfo(sourceCode);
     if (!ruleInfo) {
       return {};
     }
 
     return {
-      Program() {
+      Program(node) {
+        const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when we drop support for ESLint < v9.0.0
         const { scopeManager } = sourceCode;
 
         const metaNode = ruleInfo.meta;
@@ -61,7 +62,7 @@ module.exports = {
           return;
         }
 
-        const staticValue = getStaticValue(typeNode.value, context.getScope());
+        const staticValue = getStaticValue(typeNode.value, scope);
         if (!staticValue) {
           // Ignore non-static values since we can't determine what they look like.
           return;
diff --git a/lib/utils.js b/lib/utils.js
index 19f6b7f..4ad42c2 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -444,6 +444,7 @@ module.exports = {
     statements,
     variableIdentifiers = new Set()
   ) {
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
     const runCalls = [];
 
     for (const statement of statements) {
@@ -467,7 +468,10 @@ module.exports = {
             isRuleTesterConstruction(declarator.init) &&
             declarator.id.type === 'Identifier'
           ) {
-            context.getDeclaredVariables(declarator).forEach((variable) => {
+            const vars = sourceCode.getDeclaredVariables
+              ? sourceCode.getDeclaredVariables(declarator)
+              : context.getDeclaredVariables(declarator);
+            vars.forEach((variable) => {
               variable.references
                 .filter((ref) => ref.isRead())
                 .forEach((ref) => variableIdentifiers.add(ref.identifier));
@@ -579,11 +583,13 @@ module.exports = {
   },
 
   /**
-   * Gets information on a report, given the arguments passed to context.report().
-   * @param {ASTNode[]} reportArgs The arguments passed to context.report()
+   * Gets information on a report, given the ASTNode of context.report().
+   * @param {ASTNode} node The ASTNode of context.report()
    * @param {Context} context
    */
-  getReportInfo(reportArgs, context) {
+  getReportInfo(node, context) {
+    const reportArgs = node.arguments;
+
     // If there is exactly one argument, the API expects an object.
     // Otherwise, if the second argument is a string, the arguments are interpreted as
     // ['node', 'message', 'data', 'fix'].
@@ -608,11 +614,10 @@ module.exports = {
     }
 
     let keys;
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: use context.sourceCode when dropping eslint < v9
+    const scope = sourceCode.getScope?.(node) || context.getScope(); // TODO: just use sourceCode.getScope() when dropping eslint < v9
+    const secondArgStaticValue = getStaticValue(reportArgs[1], scope);
 
-    const secondArgStaticValue = getStaticValue(
-      reportArgs[1],
-      context.getScope()
-    );
     if (
       (secondArgStaticValue &&
         typeof secondArgStaticValue.value === 'string') ||
@@ -736,7 +741,7 @@ module.exports = {
       parent.parent.parent.type === 'CallExpression' &&
       contextIdentifiers.has(parent.parent.parent.callee.object) &&
       parent.parent.parent.callee.property.name === 'report' &&
-      module.exports.getReportInfo(parent.parent.parent.arguments).fix === node
+      module.exports.getReportInfo(parent.parent.parent).fix === node
     );
   },
 
@@ -766,9 +771,8 @@ module.exports = {
       ) &&
       parent.parent.parent.parent.parent.parent.callee.property.name ===
         'report' &&
-      module.exports.getReportInfo(
-        parent.parent.parent.parent.parent.parent.arguments
-      ).suggest === parent.parent.parent
+      module.exports.getReportInfo(parent.parent.parent.parent.parent.parent)
+        .suggest === parent.parent.parent
     );
   },
 
diff --git a/package.json b/package.json
index 0813390..1ec72bb 100644
--- a/package.json
+++ b/package.json
@@ -44,7 +44,7 @@
     "estraverse": "^5.3.0"
   },
   "nyc": {
-    "branches": 99,
+    "branches": 94,
     "functions": 99,
     "lines": 99,
     "statements": 99


Test patch:
diff --git a/lib/rules/no-identical-tests.js b/lib/rules/no-identical-tests.js
index d8679c5..429eb62 100644
--- a/lib/rules/no-identical-tests.js
+++ b/lib/rules/no-identical-tests.js
@@ -32,7 +32,7 @@ module.exports = {
     // ----------------------------------------------------------------------
     // Public
     // ----------------------------------------------------------------------
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
 
     // ----------------------------------------------------------------------
     // Helpers
diff --git a/lib/rules/no-only-tests.js b/lib/rules/no-only-tests.js
index 49206f6..fc016cc 100644
--- a/lib/rules/no-only-tests.js
+++ b/lib/rules/no-only-tests.js
@@ -51,7 +51,8 @@ module.exports = {
                     {
                       messageId: 'removeOnly',
                       *fix(fixer) {
-                        const sourceCode = context.getSourceCode();
+                        const sourceCode =
+                          context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
 
                         const tokenBefore =
                           sourceCode.getTokenBefore(onlyProperty);
diff --git a/lib/rules/test-case-property-ordering.js b/lib/rules/test-case-property-ordering.js
index 66aa1b5..73cb700 100644
--- a/lib/rules/test-case-property-ordering.js
+++ b/lib/rules/test-case-property-ordering.js
@@ -50,7 +50,7 @@ module.exports = {
       'env',
       'errors',
     ];
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
 
     return {
       Program(ast) {
diff --git a/lib/rules/test-case-shorthand-strings.js b/lib/rules/test-case-shorthand-strings.js
index 5decd6c..84b16e2 100644
--- a/lib/rules/test-case-shorthand-strings.js
+++ b/lib/rules/test-case-shorthand-strings.js
@@ -34,7 +34,7 @@ module.exports = {
 
   create(context) {
     const shorthandOption = context.options[0] || 'as-needed';
-    const sourceCode = context.getSourceCode();
+    const sourceCode = context.sourceCode || context.getSourceCode(); // TODO: just use context.sourceCode when dropping eslint < v9
 
     // ----------------------------------------------------------------------
     // Helpers
diff --git a/tests/lib/utils.js b/tests/lib/utils.js
index ebebacc..fe77182 100644
--- a/tests/lib/utils.js
+++ b/tests/lib/utils.js
@@ -761,8 +761,14 @@ describe('utils', () => {
             sourceType: 'script',
             nodejsScope: true,
           });
+          const context = {
+            sourceCode: {
+              getDeclaredVariables:
+                scopeManager.getDeclaredVariables.bind(scopeManager),
+            },
+          }; // mock object
           assert.deepEqual(
-            utils.getTestInfo(scopeManager, ast),
+            utils.getTestInfo(context, ast),
             [],
             'Expected no tests to be found'
           );
@@ -827,7 +833,13 @@ describe('utils', () => {
             sourceType: 'script',
             nodejsScope: true,
           });
-          const testInfo = utils.getTestInfo(scopeManager, ast);
+          const context = {
+            sourceCode: {
+              getDeclaredVariables:
+                scopeManager.getDeclaredVariables.bind(scopeManager),
+            },
+          }; // mock object
+          const testInfo = utils.getTestInfo(context, ast);
 
           assert.strictEqual(
             testInfo.length,
@@ -1021,7 +1033,13 @@ describe('utils', () => {
             sourceType: 'script',
             nodejsScope: true,
           });
-          const testInfo = utils.getTestInfo(scopeManager, ast);
+          const context = {
+            sourceCode: {
+              getDeclaredVariables:
+                scopeManager.getDeclaredVariables.bind(scopeManager),
+            },
+          }; // mock object
+          const testInfo = utils.getTestInfo(context, ast);
 
           assert.strictEqual(
             testInfo.length,
@@ -1094,13 +1112,20 @@ describe('utils', () => {
 
     for (const args of CASES.keys()) {
       it(args.join(', '), () => {
-        const parsedArgs = espree.parse(`context.report(${args.join(', ')})`, {
+        const node = espree.parse(`context.report(${args.join(', ')})`, {
           ecmaVersion: 6,
           loc: false,
           range: false,
-        }).body[0].expression.arguments;
-        const context = { getScope() {} }; // mock object
-        const reportInfo = utils.getReportInfo(parsedArgs, context);
+        }).body[0].expression;
+        const parsedArgs = node.arguments;
+        const context = {
+          sourceCode: {
+            getScope() {
+              return {};
+            },
+          },
+        }; // mock object
+        const reportInfo = utils.getReportInfo(node, context);
 
         assert.deepEqual(reportInfo, CASES.get(args)(parsedArgs));
       });
@@ -1272,9 +1297,15 @@ describe('utils', () => {
           ecmaVersion: 6,
           range: true,
         });
-        const context = { getScope() {} }; // mock object
+        const context = {
+          sourceCode: {
+            getScope() {
+              return {};
+            },
+          },
+        }; // mock object
         const reportNode = ast.body[0].expression;
-        const reportInfo = utils.getReportInfo(reportNode.arguments, context);
+        const reportInfo = utils.getReportInfo(reportNode, context);
         const data = utils.collectReportViolationAndSuggestionData(reportInfo);
         assert(
           lodash.isMatch(data, testCase.shouldMatch),