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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plain JS binder errors #46816

Merged
merged 6 commits into from
Nov 19, 2021
Merged

Plain JS binder errors #46816

merged 6 commits into from
Nov 19, 2021

Conversation

sandersn
Copy link
Member

@sandersn sandersn commented Nov 15, 2021

Issue some errors from the binder in JS files that do not have checkJS explicitly turned on or off. These errors mirror runtime checks done by Javascript. The checker has many more errors that also cover runtime checks. I'll start on those next.

There are a few errors in the binder that don't have matching runtime errors: they nearly always indicate incorrect code, but not always.

A label is not allowed here

I decided to include "A label is not allowed here" because, even though it's not a runtime error on its own, if you use the label it will not be defined, which is a runtime error, and because the compiler error for the undefined label is so bad.

An object literal cannot have multiple properties with the same name in strict mode

I decided not to include this error, since it's not a runtime error in ES2015 and above according to an SO answer I found. It's a useful error for TS, though; probably the error message should be changed to read "An object literal should not have multiple properties with the same name.", or else it should be restricted to ES5-only. I filed #46815 to track that.

Addresses but does not fully fix #45349.

Issue select errors from the binder in JS files that do not have checkJS
explicitly turned on or off. These errors mirror runtime checks done by
Javascript.
@sandersn
Copy link
Member Author

@mjbvz You might also have opinions about the appropriateness of these errors.

@typescript-bot typescript-bot added For Milestone Bug PRs that fix a bug with a specific milestone and removed For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Nov 15, 2021
@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Nov 15, 2021

I decided to include "A label is not allowed here" because, even though it's not a runtime error on its own, if you use the label it will not be defined, which is a runtime error, and because the compiler error for the undefined label is so bad.

#30408

I decided not to include this error, since it's not a runtime error in ES2015 and above according to an SO answer I found. It's a useful error for TS, though; probably the error message should be changed to read "An object literal should not have multiple properties with the same name.", or else it should be restricted to ES5-only. I filed #46815 to track that.

My 2 cents is to just make it a suggestion diagnostic - don't go too crazy with when and how to report this in JS, or modifying how it surfaces in TS.

@DanielRosenwasser
Copy link
Member

@typescript-bot test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 15, 2021

Heya @DanielRosenwasser, I've started to run the extended test suite on this PR at 9c85b4b. You can monitor the build here.

Copy link
Member

@amcasey amcasey left a comment

Choose a reason for hiding this comment

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

Seems sensible.

src/compiler/program.ts Show resolved Hide resolved
src/compiler/program.ts Outdated Show resolved Hide resolved
src/compiler/program.ts Outdated Show resolved Hide resolved
src/compiler/program.ts Show resolved Hide resolved
@DanielRosenwasser
Copy link
Member

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 16, 2021

Heya @DanielRosenwasser, I've started to run the tarball bundle task on this PR at 9c85b4b. You can monitor the build here.

@orta
Copy link
Contributor

orta commented Nov 16, 2021

@typescript-bot pack this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Nov 16, 2021

Heya @orta, I've started to run the tarball bundle task on this PR at 9c85b4b. You can monitor the build here.

@sandersn
Copy link
Member Author

I'm going to hold this until we have a chance to discuss this in the editor meeting tomorrow.

Comment on lines +2027 to +2028
const isCheckJs = isJs && isCheckJsEnabledForFile(sourceFile, options);
const isPlainJs = isJs && !sourceFile.checkJsDirective && options.checkJs === undefined;
Copy link
Member

Choose a reason for hiding this comment

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

What’s the difference between isCheckJsEnabledForFile the property checks you’re doing for isPlainJs?

Copy link
Member Author

Choose a reason for hiding this comment

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

I almost inlined that function to make this easier to read, except that it's used everywhere. isCheckJsEnabledForFile is true whenever checkJs is explicitly true, whether locally or globally. To put it in the most parallel form:

sourceFile.checkJsDirective?.enabled || options.checkJs === true vs
sourceFile.checkJsDirective === undefined && options.checkJs === undefined

Copy link
Member

Choose a reason for hiding this comment

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

Ah, so the third kind of JS is JS where checkJs is explicitly disabled?

Copy link
Member Author

Choose a reason for hiding this comment

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

That's right. // @ts-nocheck is the local form of that.

// - check JS: .js files with either // ts-check or checkJs: true
// - external: files that are added by plugins
const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX
|| sourceFile.scriptKind === ScriptKind.External || isPlainJs || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred);
Copy link
Member

Choose a reason for hiding this comment

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

isPlainJs || isCheckJs || ...

Related to my other question, but what other kind of JS is there?

It took me a second to realize, it’s easier to think about this condition in terms of what it excludes, although it may or may not be easier to write it that way:

  • // @ts-nocheck files
  • JSON files
  • ???

Copy link
Member Author

Choose a reason for hiding this comment

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

  • plain JS (no check options anywhere)
  • checked JS (check turned on locally or globally)
  • no-checked JS (check turned off locally)
  • TS
  • no-checked TS (check turned off locally)
  • external (from plugins?)
  • deferred (??)
  • ???

I have no idea what's in ???, that's the problem with inverting the predicate.

const includeBindAndCheckDiagnostics = !isTsNoCheck && (sourceFile.scriptKind === ScriptKind.TS || sourceFile.scriptKind === ScriptKind.TSX
|| sourceFile.scriptKind === ScriptKind.External || isPlainJs || isCheckJs || sourceFile.scriptKind === ScriptKind.Deferred);
let bindDiagnostics: readonly Diagnostic[] = includeBindAndCheckDiagnostics ? sourceFile.bindDiagnostics : emptyArray;
let checkDiagnostics = includeBindAndCheckDiagnostics ? typeChecker.getDiagnostics(sourceFile, cancellationToken) : emptyArray;
Copy link
Member Author

Choose a reason for hiding this comment

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

try out some minified or otherwise compiled JS files to make sure perf doesn't tank or crash.
(Code uses line length to disable certain features)

Checker errors require requesting diagnostics, which stll needs to be
peformance tested.

This commit removes one cross-file duplicate declaration error in the tests.
@sandersn sandersn merged commit 868c275 into main Nov 19, 2021
@sandersn sandersn deleted the plain-js-binder-errors branch November 19, 2021 01:13
@sandersn
Copy link
Member Author

On a desktop with tsserver logging enabled [1], I created a simple project, then opened a new Typescript file and pasted in some code [2].
I opened ajv.min.js, a single-line 121k file, and switched back to my test file. Goto-def was responsive after a second or so.
Then I opened Typescript's own run.js that we use to run tests, a 14 MB file, and switched back to my test file. Goto-def was responsive after 2-3 seconds.

  • In both cases, as soon goto-def was responsive, the rest of the language service was responsive.
  • Code notified me that it was not tokenising ajv.min.js, and there was no syntax highlighting.
  • It took considerably longer to get quick info from run.js itself when I switched back to it. Probably another 10 seconds?

Based on this:

  1. I think it's fine to proceed with the simple approach of requesting semantic diagnostics for plain JS files. If a particularly slow use case turns up in the beta period, we'll have a couple of ways to fix it.
  2. I'll try to find out why grammar errors are in the checker and see how hard it would be to move them earlier. They make up about 20% of the errors in the checker, and most of the value for plain JS is probably in the grammar errors.

[1] So, faster because it's a desktop; slower because logging is on.
[2] Just some random code I worked on recently.

export class C {
    set x(...n) { }
}
declare var x: Promise<number>
await x
const y = await x

class Test {
    foo() {

    }

    bar() {
        this.blargh(this, "completely wrong") // Can't autocomplete second parameter

        const t = new Test()
        this.blargh(t, "bar"); // Second parameter autocompletes to "foo", "blargh", "bar"
    }

    blargh<T>(a: T, k: keyof T) {
        // some implementation
    }
}

const t = new Test()
t.blargh(t, "bar")
t.blargh(t, "hi")

@amcasey
Copy link
Member

amcasey commented Nov 19, 2021

@sandersn How long did it take for GTD to become usable without the diagnostics? (Sorry if I just missed that in your write-up.)

If we think we might have to roll this back (contextually?), would it be useful to have a configuration option so that editors can add an escape hatch?

@sandersn
Copy link
Member Author

Good question. After opening ajv.min.js, a lot less than a second. After opening run.js, around 2 seconds -- there probably was a difference from the semantics-requesting version, but not one that I could measure without a stopwatch.

@DanielRosenwasser
Copy link
Member

If a particularly slow use case turns up in the beta period, we'll have a couple of ways to fix it.

How do you expect most JavaScript users, who don't opt into nightly/beta versions of TypeScript in their editor, to provide feedback during the beta period?

@amcasey
Copy link
Member

amcasey commented Nov 19, 2021

I think @andrewbranch looked into ways to monitor completion performance via telemetry when he was working on auto-imports. Maybe that would help?

@sandersn
Copy link
Member Author

sandersn commented Dec 6, 2021

Now that I think about it, getSuggestionDiagnostics is called on plain JS files, and it checks the entire file as well. Calling getDiagnostics shouldn't check the file twice, so calling it shouldn't add much time at all.

@mjbvz Can you confirm how much getSuggestionDiagnostics is called on plain JS files compared to getDiagnostics on TS files? I poked around vscode's typescript code but couldn't figure it out. From usage, it seems like suggestions show up on plain JS files at the same rate that errors do on TS files.

@mjbvz
Copy link
Contributor

mjbvz commented Dec 7, 2021

@sandersn VS Code uses the geterr TS Server command to get all the errors for a file instead of requesting each type of diagnostic individually. Is that what you were asking about?

@sandersn
Copy link
Member Author

sandersn commented Dec 7, 2021

Pretty much--I want to know whether it's calling geterr on JS files as often as it is on TS files. I suspect the answer is yes, in order for it to be able to show suggestions in JS files.

@mjbvz
Copy link
Contributor

mjbvz commented Dec 7, 2021

Yes we should have the same code path for both JS and TS on the VS Code side

mprobst pushed a commit to mprobst/TypeScript that referenced this pull request Jan 10, 2022
* Plain JS binder errors

Issue select errors from the binder in JS files that do not have checkJS
explicitly turned on or off. These errors mirror runtime checks done by
Javascript.

* Rest of plain JS binder errors

* address PR comments

* Only issue binder errors in plain JS.

Checker errors require requesting diagnostics, which stll needs to be
peformance tested.

This commit removes one cross-file duplicate declaration error in the tests.

* fix const lint
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Milestone Bug PRs that fix a bug with a specific milestone
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide Grammar Errors for JavaScript Files
7 participants