Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin): [unbound-method] support bound builtins #1526

Merged
merged 7 commits into from Feb 3, 2020
Merged

feat(eslint-plugin): [unbound-method] support bound builtins #1526

merged 7 commits into from Feb 3, 2020

Conversation

G-Rath
Copy link
Contributor

@G-Rath G-Rath commented Jan 25, 2020

This was suspiciously too easy 馃暤

Sadly @bradzacher imports are seemingly not considered decorations - let me know how you'd like to handle that case.

I found that I could compare against valueDeclaration, rather than using symbol.getDeclarations(), but it's quite possible that I've not tested it against a complex enough setup for that to fail, so happy to use the original suggested method for tackling this :)

closes #1085

@typescript-eslint

This comment has been minimized.

@bradzacher bradzacher added the enhancement New feature or request label Jan 26, 2020
@bradzacher
Copy link
Member

imports are seemingly not considered declarations

It took me a minute to figure this out.

The import declaration doesn't count as a declaration, because we're inspecting the symbol console.log. The .log method doesn't have an import declaration - it only has its declaration as a method, in the other file!

Soooooooooooooo with that in mind; what's the objective we want here; we want to be able to whitelist certain methods as being always "safe", right?

What's our whitelist? Something like this:

const validMemberExpressions = [
  'JSON.parse',
  'JSON.stringify',
  Object.keys(console)
    .filter(name => !name.startsWith('_'))
    .map(name => `console.${name}`),
]

First up, let's make an assumption to make things simpler for us - we're not going to support aliases for now (i.e. const foo = { log: console.log }; promise.then(foo.log) will throw an error).

This then becomes a pretty simple problem to solve:

  1. ensure the member expression matches one of validMemberExpressions
  2. check that the root object of the member expression was not imported

(1) you can do either via explicitly checking the structure of the AST, or via comparing the source code with whitespace removed.

(2) will be something like:

  • get the root .object of the member expression (which should be an Identifier).
  • get the Identifier's symbol, and its declaration.
  • ensure the declaration's source file is not this file.

Note - you should also setup a test case which uses valid imports.

In packages/eslint-plugin/tests/fixtures/class.ts, add something like

// used by unbound-method test case to test imports
export const console = { log() {} };

Then in your tests, make sure you're importing that, not the unknown file consoleshim.

import { console } from './class'
const x = console.log;

@bradzacher bradzacher added the awaiting response Issues waiting for a reply from the OP or another party label Jan 26, 2020
Copy link
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rc for last comment

@G-Rath
Copy link
Contributor Author

G-Rath commented Jan 31, 2020

@bradzacher guess who went a little overboard on the whitelist... :D

Also, it seems that valueDeclaration is not defined when the symbol is imported O.o

If that's true, that might be of use elsewhere?

@bradzacher bradzacher removed the awaiting response Issues waiting for a reply from the OP or another party label Jan 31, 2020
@bradzacher
Copy link
Member

guess who went a little overboard on the whitelist

Hey dude, that's actually awesome. Will save a lot of noise for people!

Note (it won't matter for long, as we'll drop node 8 in v3...) BigInt isn't available in node 8, so you'll have to do a check for that.

it seems that valueDeclaration is not defined when the symbol is imported

That's interesting. TBH IDK what the difference between declarations and valueDeclaration is. There is a distinct lack of docs for this stuff, and the comment in the source code is just "First value declaration of the symbol".

@G-Rath
Copy link
Contributor Author

G-Rath commented Jan 31, 2020

Hey dude, that's actually awesome. Will save a lot of noise for people!

Yeah, I think the only one I'm missing right now is ArrayBuffer, or something 馃槀

Note (it won't matter for long, as we'll drop node 8 in v3...) BigInt isn't available in node 8, so you'll have to do a check for that.

I'll just comment it out for now, and we can enable it when we release v3.

That's interesting.

Yeah, it was actually confusing the heck out of me at first b/c I thought it was the !== condition that was being weird, but I ran it through ts-ast-viewer and confirmed that when you add an import, the property magically vanishes 馃し鈥嶁檪

Can you restart the CI? It's randomly failed again like it did for me a few days back 馃槵

Oh, no it's b/c of BigInt. Woops

Copy link
Member

@bradzacher bradzacher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few final comments.
Otherwise LGTM.
Thanks for doing this.

packages/eslint-plugin/src/rules/unbound-method.ts Outdated Show resolved Hide resolved
packages/eslint-plugin/src/rules/unbound-method.ts Outdated Show resolved Hide resolved
packages/eslint-plugin/src/rules/unbound-method.ts Outdated Show resolved Hide resolved
packages/eslint-plugin/tests/rules/unbound-method.test.ts Outdated Show resolved Hide resolved
packages/eslint-plugin/src/rules/unbound-method.ts Outdated Show resolved Hide resolved
@bradzacher bradzacher changed the title fix(eslint-plugin): compare source file to declaration for unbound check feat(eslint-plugin): [unbound-method] support bound builtins Feb 3, 2020
@bradzacher bradzacher merged commit 0a110eb into typescript-eslint:master Feb 3, 2020
@bradzacher
Copy link
Member

Thanks for doing this!

bradzacher added a commit that referenced this pull request Feb 3, 2020
#1526 Added a whitelist of member expressions which are bound by default .
But of the ~100 methods in the list, ~15 aren't actually bound (for whatever reason).
This just adds a blacklist alongside the whitelist so we don't false-negative.
@G-Rath G-Rath deleted the fix-unbound-method branch February 3, 2020 18:56
@G-Rath
Copy link
Contributor Author

G-Rath commented Feb 3, 2020

Thanks for doing this!

No problem! Apologies for not getting back around to it sooner, but cheers for picking it up for the release! :D

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 20, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[unbound-method] console methods are bound
3 participants