Repository: ember-cli/eslint-plugin-ember (js)

Issue:
no-invalid-debug-function-arguments is not checking import source
```js
import { deprecate } from './-utils';

import isHtmlSafe from './string/is-html-safe';

export default deprecate(isHtmlSafe, 'isHtmlSafe', 'string.isHtmlSafe');
```

I have my own deprecate function, but the rule is triggering on it.

Pull request:
Validate imports before reporting violations in `no-invalid-debug-function-arguments` rule
Add import checks to ensure that this rule only flags the real Ember debug functions, not locally-defined ones that happen to have the same name.

Fixes #604. Helps with #590.

Patch:
diff --git a/lib/rules/no-invalid-debug-function-arguments.js b/lib/rules/no-invalid-debug-function-arguments.js
index bb450caf..81489935 100644
--- a/lib/rules/no-invalid-debug-function-arguments.js
+++ b/lib/rules/no-invalid-debug-function-arguments.js
@@ -1,15 +1,14 @@
 'use strict';
 
 const types = require('../utils/types');
-const emberUtils = require('../utils/ember');
+const { getImportIdentifier } = require('../utils/import');
 
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
 
-function getErrorMessage(debugFunction) {
-  return `Usage of Ember's \`${debugFunction}\` function has its arguments passed in the wrong order. \`String description\` should come before \`Boolean condition\`.`;
-}
+const ERROR_MESSAGE =
+  'This Ember debug function has its arguments passed in the wrong order. `String description` should come before `Boolean condition`.';
 
 const DEBUG_FUNCTIONS = ['assert', 'deprecate', 'warn'];
 
@@ -28,16 +27,29 @@ module.exports = {
     schema: [],
   },
 
-  getErrorMessage,
+  ERROR_MESSAGE,
   DEBUG_FUNCTIONS,
 
   create(context) {
+    let importedIdentifiers = [];
+
     return {
+      ImportDeclaration(node) {
+        if (node.source.value !== '@ember/debug') {
+          return;
+        }
+
+        // Gather the identifiers that these functions are imported under.
+        importedIdentifiers = DEBUG_FUNCTIONS.map(fn =>
+          getImportIdentifier(node, '@ember/debug', fn)
+        );
+      },
+
       CallExpression(node) {
-        if (isDebugFunctionWithReversedArgs(node)) {
+        if (isDebugFunctionWithReversedArgs(node, importedIdentifiers)) {
           context.report({
             node,
-            message: getErrorMessage(getDebugFunction(node)),
+            message: ERROR_MESSAGE,
           });
         }
       },
@@ -45,23 +57,32 @@ module.exports = {
   },
 };
 
-function isDebugFunctionWithReversedArgs(node) {
+function isDebugFunctionWithReversedArgs(node, importedIdentifiers) {
   return (
-    isDebugFunction(node) &&
+    isDebugFunction(node, importedIdentifiers) &&
     node.arguments.length >= 2 &&
     !types.isString(node.arguments[0]) &&
     types.isString(node.arguments[1])
   );
 }
 
-function isDebugFunction(node) {
-  return getDebugFunction(node) !== undefined;
+function isDebugFunction(node, importedIdentifiers) {
+  return getDebugFunction(node, importedIdentifiers) !== undefined;
 }
 
-function getDebugFunction(node) {
-  return DEBUG_FUNCTIONS.find(
+function getDebugFunction(node, importedIdentifiers) {
+  const isEmberDebugFunctionCall = DEBUG_FUNCTIONS.find(
     debugFunction =>
-      emberUtils.isModule(node, debugFunction) &&
-      (!node.callee.property || node.callee.property.name === debugFunction)
+      types.isMemberExpression(node.callee) &&
+      types.isIdentifier(node.callee.object) &&
+      node.callee.object.name === 'Ember' &&
+      types.isIdentifier(node.callee.property) &&
+      node.callee.property.name === debugFunction
+  );
+
+  const isImportedDebugFunctionCall = importedIdentifiers.find(
+    debugFunction => types.isIdentifier(node.callee) && node.callee.name === debugFunction
   );
+
+  return isEmberDebugFunctionCall || isImportedDebugFunctionCall;
 }


Test patch:
diff --git a/tests/lib/rules/no-invalid-debug-function-arguments.js b/tests/lib/rules/no-invalid-debug-function-arguments.js
index d7dbc38d..34ce8d86 100644
--- a/tests/lib/rules/no-invalid-debug-function-arguments.js
+++ b/tests/lib/rules/no-invalid-debug-function-arguments.js
@@ -5,7 +5,7 @@
 const rule = require('../../../lib/rules/no-invalid-debug-function-arguments');
 const RuleTester = require('eslint').RuleTester;
 
-const { DEBUG_FUNCTIONS, getErrorMessage } = rule;
+const { DEBUG_FUNCTIONS, ERROR_MESSAGE } = rule;
 
 //------------------------------------------------------------------------------
 // Tests
@@ -23,43 +23,51 @@ const VALID_USAGES_BASIC = [
 const VALID_USAGES_FOR_EACH_DEBUG_FUNCTION = flatten(
   DEBUG_FUNCTIONS.map(debugFunction => [
     {
-      code: `OtherClass.${debugFunction}(true, 'My string.');`,
+      code: `import { ${debugFunction} } from '@ember/debug'; OtherClass.${debugFunction}(true, 'My string.');`,
     },
     {
-      code: `${debugFunction}();`,
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}();`,
     },
     {
       code: `Ember.${debugFunction}();`,
     },
     {
-      code: `${debugFunction}('My description.');`,
+      // Missing import:
+      code: `${debugFunction}(true, 'My string.');`,
+    },
+    {
+      // Wrong import:
+      code: `import { ${debugFunction} } from 'something-else'; ${debugFunction}(true, 'My string.');`,
+    },
+    {
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}('My description.');`,
     },
     {
       code: `Ember.${debugFunction}('My description.');`,
     },
     {
-      code: `${debugFunction}('My description.', true);`,
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}('My description.', true);`,
     },
     {
       code: `Ember.${debugFunction}('My description.', true);`,
     },
     {
-      code: `${debugFunction}('My description.', true, { id: 'some-id' });`,
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}('My description.', true, { id: 'some-id' });`,
     },
     {
       code: `Ember.${debugFunction}('My description.', true, { id: 'some-id' });`,
     },
     {
-      code: `const CONDITION = true; ${debugFunction}('My description.', CONDITION);`,
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}('My description.', CONDITION);`,
     },
     {
-      code: `const DESCRIPTION = 'description'; ${debugFunction}(DESCRIPTION, true);`,
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(DESCRIPTION, true);`,
     },
     {
-      code: `const DESCRIPTION = 'description'; const CONDITION = true; ${debugFunction}(DESCRIPTION, CONDITION);`,
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(DESCRIPTION, CONDITION);`,
     },
     {
-      code: `${debugFunction}.unrelatedFunction(true, 'My description.');`,
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}.unrelatedFunction(true, 'My description.');`,
     },
     {
       code: `Ember.${debugFunction}.unrelatedFunction(true, 'My description.');`,
@@ -72,39 +80,39 @@ const VALID_USAGES = [...VALID_USAGES_BASIC, ...VALID_USAGES_FOR_EACH_DEBUG_FUNC
 const INVALID_USAGES = flatten(
   DEBUG_FUNCTIONS.map(debugFunction => [
     {
-      code: `${debugFunction}(true, 'My description.');`,
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(true, 'My description.');`,
       output: null,
-      errors: [{ message: getErrorMessage(debugFunction), type: 'CallExpression' }],
+      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
     },
     {
       code: `Ember.${debugFunction}(true, 'My description.');`,
       output: null,
-      errors: [{ message: getErrorMessage(debugFunction), type: 'CallExpression' }],
+      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
     },
     {
-      code: `${debugFunction}(true, 'My description.', { id: 'some-id' });`,
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(true, 'My description.', { id: 'some-id' });`,
       output: null,
-      errors: [{ message: getErrorMessage(debugFunction), type: 'CallExpression' }],
+      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
     },
     {
       code: `Ember.${debugFunction}(true, 'My description.', { id: 'some-id' });`,
       output: null,
-      errors: [{ message: getErrorMessage(debugFunction), type: 'CallExpression' }],
+      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
     },
     {
-      code: `${debugFunction}(true, \`My \${123} description.\`);`,
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(true, \`My \${123} description.\`);`,
       output: null,
-      errors: [{ message: getErrorMessage(debugFunction), type: 'CallExpression' }],
+      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
     },
     {
-      code: `const CONDITION = true; ${debugFunction}(CONDITION, 'My description.');`,
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(CONDITION, 'My description.');`,
       output: null,
-      errors: [{ message: getErrorMessage(debugFunction), type: 'CallExpression' }],
+      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
     },
     {
-      code: `const CONDITION = true; ${debugFunction}(CONDITION, \`My \${123} description.\`);`,
+      code: `import { ${debugFunction} } from '@ember/debug'; ${debugFunction}(CONDITION, \`My \${123} description.\`);`,
       output: null,
-      errors: [{ message: getErrorMessage(debugFunction), type: 'CallExpression' }],
+      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }],
     },
   ])
 );