Skip to content
This repository has been archived by the owner on Dec 13, 2018. It is now read-only.

Handle null values in linters wrapped by the LinterAdapter #906

Closed
wants to merge 5 commits into from

Conversation

oleander
Copy link
Contributor

@oleander oleander commented Nov 27, 2016

The linter provided by steelbrain/linter allows for null values to be returned by a provider, while the current LinterAdapter wrapping it, doesn't. Here's the relevant code in steelbrain/linter.

The linter-eslint package is currently returning null (pull request/discussion, current implementation).

Example usage:

// ...
export function provideLinter() {
  return {
    // ...
    lint(editor) {
      // ...
      return eslint.lint(text).then(function(result) {
        if (isTooOld(result)) return null;
        return result;
      });
    }
  }
}

The current implementation of Nuclide throws the following error on null when using the latest version of linter-eslint.

atom/packages/nuclide/pkg/nuclide-diagnostics-store/lib/LinterAdapter.js:86 Uncaught (in promise) TypeError: Cannot read property 'Symbol(Symbol.iterator)' of null
  at linterMessagesToDiagnosticUpdate (atom/packages/nuclide/pkg/nuclide-diagnostics-store/lib/LinterAdapter.js:86:21)
  at atom/packages/nuclide/pkg/nuclide-diagnostics-store/lib/LinterAdapter.js:164:36
  at undefined.next (<anonymous>:null:null)
  at step (atom/packages/nuclide/node_modules/async-to-generator/async-to-generator.js:11:30)
  at atom/packages/nuclide/node_modules/async-to-generator/async-to-generator.js:21:13

This PR aims to solve this by checking the return value of a linter.

@facebook-github-bot
Copy link

By analyzing the blame information on this pull request, we identified @zertosh, @hansonw and @tyangliu to be potential reviewers.

@facebook-github-bot
Copy link

@hansonw has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@nmote
Copy link
Contributor

nmote commented Nov 28, 2016

In steelbrain/linter, what happens when a provider returns null, or a promise that resolves to null? Is it the same as it returning an empty set of lints?

As implemented, this PR means that if a provider returns some results, then on a subsequent call returns null, the previous results will remain visible. Is that intentional, or do you want to invalidate the current messages so that nothing is displayed?

@hansonw
Copy link
Contributor

hansonw commented Nov 28, 2016

@nmote The linter package does what you mentioned in its implementation here: https://github.com/steelbrain/linter/blob/v1/lib/linter-registry.js#L65 so I think that's reasonable. I'd imagine you would return empty to invalidate the current messages.

@nmote
Copy link
Contributor

nmote commented Nov 28, 2016

Perfect, then 👍

}

const result = await this._requestSerializer.run(maybe);
if (!result) {
Copy link
Contributor

Choose a reason for hiding this comment

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

RequestSerializer::run never returns null or undefined. If the underlying promise resolves to null it will return an object with a null result field.

Instead you'll need to check linterMessages (below).

This change should also change the types in nuclide-diagnostics-common/lib/main.js to reflect the actual lint return type. Then Flow should guide you through the necessary changes.

@oleander
Copy link
Contributor Author

Thanks for your response guys. Before I continue, could you shed some light on these errors I get running scripts/dev/test --no-version on master.

Are they "by design", or did I miss a build step? I don't get any errors related to LinterAdapter.js before or after my commit.

☺  scripts/dev/test --no-version
Running lint_packages...
Finished lint_packages (0 seconds)
Running flow...
pkg/commons-node/passesGK.js:23
 23:     Gatekeeper = require('./fb-gatekeeper').Gatekeeper;
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb-gatekeeper. Required module not found

pkg/commons-node/process.js:436
436:   FB_INCLUDE_PATHS = require('./fb-config').FB_INCLUDE_PATHS;
                          ^^^^^^^^^^^^^^^^^^^^^^ ./fb-config. Required module not found

pkg/hyperclick/lib/main.js:29
 29:     const {overrideGoToDeclaration} = require('./fb/overrideGoToDeclaration');
                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/overrideGoToDeclaration. Required module not found

pkg/nuclide-analytics/lib/track.js:20
 20:   module.exports = require('../fb/analytics');
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ ../fb/analytics. Required module not found

pkg/nuclide-arcanist/lib/ArcBuildSystem.js:52
 52:       ArcToolbarModel = require('./fb/FbArcToolbarModel').FbArcToolbarModel;
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/FbArcToolbarModel. Required module not found

pkg/nuclide-blame-provider-hg/lib/HgBlameProvider.js:104
104:   const {getPhabricatorUrlForRevision} = require('./fb/FbHgBlameProvider');
                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/FbHgBlameProvider. Required module not found

pkg/nuclide-clang-rpc/lib/ClangFlagsManager.js:76
 76:       _overrideIncludePath = require('./fb/custom-flags').overrideIncludePath;
                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/custom-flags. Required module not found

pkg/nuclide-clang-rpc/lib/ClangServerManager.js:35
 35:       _getDefaultFlags = require('./fb/custom-flags').getDefaultFlags;
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/custom-flags. Required module not found

pkg/nuclide-clang-rpc/lib/find-clang-server-args.js:29
 29:       fbFindClangServerArgs = require('./fb/find-clang-server-args');
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/find-clang-server-args. Required module not found

pkg/nuclide-debugger-native/lib/LLDBLaunchAttachProvider.js:42
 42:       this._loadAction(require('./actions/fb-omActionUIProvider'));
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./actions/fb-omActionUIProvider. Required module not found

pkg/nuclide-debugger-php/lib/AttachProcessInfo.js:36
 36:       const services = require('./fb/services');
                            ^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/services. Required module not found

pkg/nuclide-diff-view/lib/DiffViewComponent.js:62
 62:       const {DiffViewPublishForm} = require('./fb/DiffViewPublishForm');
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/DiffViewPublishForm. Required module not found

pkg/nuclide-diff-view/lib/DiffViewComponent.js:77
 77:       const {DiffViewCreateForm} = require('./fb/DiffViewCreateForm');
                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/DiffViewCreateForm. Required module not found

pkg/nuclide-diff-view/lib/RevisionTimelineNode.js:94
 94:       const diffUtils = require('../../commons-node/fb-vcs-utils.js');
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ../../commons-node/fb-vcs-utils.js. Required module not found

pkg/nuclide-diff-view/lib/redux/Epics.js:722
722:   Object.assign(module.exports, require('../fb/Epics'));
                                     ^^^^^^^^^^^^^^^^^^^^^^ ../fb/Epics. Required module not found

pkg/nuclide-flow/lib/FlowServiceWatcher.js:69
 69:     const reportButton = await require('./fb-report-crash').getButton();
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb-report-crash. Required module not found

pkg/nuclide-hg-repository-client/lib/HgRepositoryClient.js:81
 81:     const FbRevisionStatusCache = require('./fb/RevisionStatusCache');
                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/RevisionStatusCache. Required module not found

pkg/nuclide-hg-rpc/lib/HgService.js:197
197:     refinements = require('./fb/config').primaryWatchSubscriptionRefinements;
                       ^^^^^^^^^^^^^^^^^^^^^^ ./fb/config. Required module not found

pkg/nuclide-nux/lib/NuxStore.js:44
 44:       NuxBackendCache = require('./fb-NuxCache').NuxCache;
                             ^^^^^^^^^^^^^^^^^^^^^^^^ ./fb-NuxCache. Required module not found

pkg/nuclide-python-rpc/lib/JediServerManager.js:27
 27:     overrides = await require('./fb/find-jedi-server-args')(src);
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/find-jedi-server-args. Required module not found

pkg/nuclide-python-rpc/lib/PythonService.js:100
100:     const overridePath = require('./fb/find-formatter-path')();
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/find-formatter-path. Required module not found

pkg/nuclide-python-rpc/lib/PythonService.js:184
184:     result = await require('./fb/run-flake8')(src, contents, configPath);
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ ./fb/run-flake8. Required module not found

pkg/nuclide-remote-projects/lib/connection-profile-utils.js:160
160:     defaultConfigGetter = require('./fb/config');
                               ^^^^^^^^^^^^^^^^^^^^^^ ./fb/config. Required module not found


Found 23 errors
☃  atom --version                                                                                                                                [oleander@laptop][14:07:39]
Atom    : 1.12.5
Electron: 1.3.9
Chrome  : 52.0.2743.82
Node    : 6.5.0
☺  git rev-parse HEAD # master
abddcc12fd9d9f5e61ffde1be95008b0c3b6f9db
☺  flow version
Flow, a static type checker for JavaScript, version 0.35.0

@nmote
Copy link
Contributor

nmote commented Nov 29, 2016

That's our fault, and frankly it's something I'm really unhappy about. We have various places where for the sake of expedience we've added some facebook-internal functionality into these fb- prefixed files, which ship with our internal version but do not get synced to GitHub. This means that when running Flow on the GitHub copy, you will see these errors.

I don't have a great solution for you, to be honest. Probably just run Flow yourself and ignore these errors. And for running tests, just run the tests for the package you are working on.

Fixing this properly will take a concerted effort on our part. A lot of us want to do it but we just haven't had the time. In the meantime if you have ideas for how to ease the pain I would be more than happy to review pull requests (maybe adding options to the script or something). Feel free to open issues to discuss ideas and CC me so I get to them promptly.

@hansonw
Copy link
Contributor

hansonw commented Nov 29, 2016

For the time being a lot of these fb-only requires are preceded by a // $FlowFB so uncommenting this line https://github.com/facebook/nuclide/blob/master/.flowconfig#L31 in the .flowconfig may reduce the noise.

@nmote
Copy link
Contributor

nmote commented Nov 29, 2016

But, I don't think we've been diligent about adding those comments so it might not get all of them. And be careful not to include the config change in your PR :/

* https://github.com/facebook/nuclide: (106 commits)
  Consolidate text-buffer utils
  Rename buffer.js -> text-buffer.js
  Revert Confirmation: Ask for confirmation before reverting from file changes lists
  Transform buck test failures into diagnostics
  Throw with a descriptive error when a circular transpile is found
  Do not use maybeToString in nuclide-logging's stacktrace
  Move to Workspace View
  Task Runner: further reduce unnecessary re-renders
  require language-graphql package to be installed for using as an activation hook
  fix the font generator to set lower baseline && normalize the icon sizes
  add GraphQL logo to nuclicons
  Create throttle operator
  Navigation Stack in the FAQ and keyboard shortcuts
  Add uncommitted changes to source control sidebar
  Ask for confirmation before reverting a file
  Add highlight when dragging over textareas
  Actually add file to Mercurial rather than revert
  Add `shorten` to node-commons
  s/trackOperationTiming/trackTiming/g
  Fix insertText return value
  ...
@facebook-github-bot
Copy link

@oleander updated the pull request - view changes - changes since last import

@oleander
Copy link
Contributor Author

oleander commented Dec 1, 2016

@nmote Now everything passed.

Copy link
Contributor

@nmote nmote left a comment

Choose a reason for hiding this comment

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

Looks good overall -- just a couple small things before we merge.

editor.getPath(),
linterMessages, this._provider.providerName || this._provider.name,
);
if (!this._enabled) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Good call with the early returns here. It reads much better.

return;
}

if (this._provider.invalidateOnClose && !this._onDestroyDisposables.has(buffer)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Any particular reason why this block happens before the linterMessages null check? Or did you just choose the order arbitrarily?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tried to make as small changes as possible until the spec passed. The linterMessages = result.result row was placed below the block so kept it there, just in case. Do you want me to move it?

The code would look more symmetrical if the guard was moved above the block, so I don't mind moving it.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't really care either way. I don't think it's important. I just wanted to make sure there wasn't some subtlety I was overlooking.

}

const maybe = this._provider.lint(editor);
if (!maybe) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We prefer to do null/undefined checks explicitly: if (maybe == null). Better to write what you actually mean even though it's a few more characters, rather than relying on truthiness which has some odd quirks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Aha, okay. I'm so used to ESLint complaining about not using === so almost forgot about == null being the same as === undefined || === null.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, that's the one exception we make in our config. It's basically due to Flow's ?something being shorthand for something | null | undefined. Since it's used so pervasively we basically have to conflate null and undefined. Oh well.

@facebook-github-bot
Copy link

@oleander updated the pull request - view changes - changes since last import

@oleander
Copy link
Contributor Author

oleander commented Dec 1, 2016

Fixed all bugs™

@nmote
Copy link
Contributor

nmote commented Dec 1, 2016

Thanks, LGTM

facebook-github-bot pushed a commit that referenced this pull request Dec 1, 2016
Summary:
Add notice about the `$FlowFB` tag used in flow for non Facebook employees (see: #906 (comment)) to avoid errors similar to those listed here: #906 (comment).

This also fixes tree flow errors related to missing, misspelled or unused `$FlowFB` tags.

Ping hansonw.
Closes #912

Differential Revision: D4261281

Pulled By: nmote

fbshipit-source-id: 987646425485c8734311c117fcf69a45cd6dde2c
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants