Skip to content

Commit

Permalink
fix(eslint-plugin): [unbound-method] handling destructuring (#2228)
Browse files Browse the repository at this point in the history
  • Loading branch information
yeonjuan authored Jun 20, 2020
1 parent f5c271f commit c3753c2
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 4 deletions.
46 changes: 44 additions & 2 deletions packages/eslint-plugin/src/rules/unbound-method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const nativelyBoundMembers = SUPPORTED_GLOBALS.map(namespace => {
.reduce((arr, names) => arr.concat(names), [])
.filter(name => !nativelyNotBoundMembers.has(name));

const isMemberNotImported = (
const isNotImported = (
symbol: ts.Symbol,
currentSourceFile: ts.SourceFile | undefined,
): boolean => {
Expand Down Expand Up @@ -176,7 +176,7 @@ export default util.createRule<Options, MessageIds>({
if (
objectSymbol &&
nativelyBoundMembers.includes(getMemberFullName(node)) &&
isMemberNotImported(objectSymbol, currentSourceFile)
isNotImported(objectSymbol, currentSourceFile)
) {
return;
}
Expand All @@ -191,6 +191,48 @@ export default util.createRule<Options, MessageIds>({
});
}
},
'VariableDeclarator, AssignmentExpression'(
node: TSESTree.VariableDeclarator | TSESTree.AssignmentExpression,
): void {
const [idNode, initNode] =
node.type === AST_NODE_TYPES.VariableDeclarator
? [node.id, node.init]
: [node.left, node.right];

if (initNode && idNode.type === AST_NODE_TYPES.ObjectPattern) {
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(initNode);
const rightSymbol = checker.getSymbolAtLocation(tsNode);
const initTypes = checker.getTypeAtLocation(tsNode);

const notImported =
rightSymbol && isNotImported(rightSymbol, currentSourceFile);

idNode.properties.forEach(property => {
if (
property.type === AST_NODE_TYPES.Property &&
property.key.type === AST_NODE_TYPES.Identifier
) {
if (
notImported &&
util.isIdentifier(initNode) &&
nativelyBoundMembers.includes(
`${initNode.name}.${property.key.name}`,
)
) {
return;
}

const symbol = initTypes.getProperty(property.key.name);
if (symbol && isDangerousMethod(symbol, ignoreStatic)) {
context.report({
messageId: 'unbound',
node,
});
}
}
});
}
},
};
},
});
Expand Down
141 changes: 139 additions & 2 deletions packages/eslint-plugin/tests/rules/unbound-method.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,35 @@ class A {
}
}
`,
'const { parseInt } = Number;',
'const { log } = console;',
`
let parseInt;
({ parseInt } = Number);
`,
`
let log;
({ log } = console);
`,
`
const foo = {
bar: 'bar',
};
const { bar } = foo;
`,
`
class Foo {
unbnound() {}
bar = 4;
}
const { bar } = new Foo();
`,
`
class Foo {
bound = () => 'foo';
}
const { bound } = new Foo();
`,
],
invalid: [
{
Expand Down Expand Up @@ -288,8 +317,8 @@ function foo(arg: ContainsMethods | null) {
'const unbound = instance.unbound;',
'const unboundStatic = ContainsMethods.unboundStatic;',

'const { unbound } = instance.unbound;',
'const { unboundStatic } = ContainsMethods.unboundStatic;',
'const { unbound } = instance;',
'const { unboundStatic } = ContainsMethods;',

'<any>instance.unbound;',
'instance.unbound as any;',
Expand Down Expand Up @@ -384,5 +413,113 @@ const unbound = new Foo().unbound;
},
],
},
{
code: `
class Foo {
unbound() {}
}
const { unbound } = new Foo();
`,
errors: [
{
line: 5,
messageId: 'unbound',
},
],
},
{
code: `
class Foo {
unbound = function () {};
}
const { unbound } = new Foo();
`,
errors: [
{
line: 5,
messageId: 'unbound',
},
],
},
{
code: `
class Foo {
unbound() {}
}
let unbound;
({ unbound } = new Foo());
`,
errors: [
{
line: 6,
messageId: 'unbound',
},
],
},
{
code: `
class Foo {
unbound = function () {};
}
let unbound;
({ unbound } = new Foo());
`,
errors: [
{
line: 6,
messageId: 'unbound',
},
],
},
{
code: `
class CommunicationError {
foo() {}
}
const { foo } = CommunicationError.prototype;
`,
errors: [
{
line: 5,
messageId: 'unbound',
},
],
},
{
code: `
class CommunicationError {
foo() {}
}
let foo;
({ foo } = CommunicationError.prototype);
`,
errors: [
{
line: 6,
messageId: 'unbound',
},
],
},
{
code: `
import { console } from './class';
const { log } = console;
`,
errors: [
{
line: 3,
messageId: 'unbound',
},
],
},
{
code: 'const { all } = Promise;',
errors: [
{
line: 1,
messageId: 'unbound',
},
],
},
],
});

0 comments on commit c3753c2

Please sign in to comment.