-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Add visible alias statements for non-visible binding patterns when emitting declaration #48869
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
Add visible alias statements for non-visible binding patterns when emitting declaration #48869
Conversation
…itting declaration
&& isDeclarationVisible(declaration.parent.parent.parent.parent.parent)) { | ||
return addVisibleAlias(declaration, declaration.parent.parent.parent.parent); | ||
} | ||
else if (symbol.flags & SymbolFlags.BlockScopedVariable) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the new code is only within this else if
block
const { Foo } = require("./a"); | ||
export default class extends Foo {} | ||
~~~ | ||
!!! error TS4021: 'extends' clause of exported class has or is using private name 'Foo'. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error is gone here as now destructured variables can be visible - so this is a good change. However, maybe this test should be rewritten somehow but I'm not sure how. How unique it was? There are already some other tests with this error reported for the extends
.
This test was introduced in #39567 and later tweaked in #39631 . Maybe @a-tarasyuk could give me some insight here.
Also note that now the declaration for this test case is still not emitted - the emit output didn't change, you can see it here:
https://github.com/microsoft/TypeScript/blob/61bdfa9697f638e0eb94ca57ec4204139b9a0dbd/tests/baselines/reference/declarationEmitExpressionInExtends6.js
I'm not sure if this should be emitted or not, the mixed TS+JS situation is confusing to me and I have no expectations of my own when it comes to such a setup. No other error is raised here despite the .d.ts
not being generated - which looks suspicious to me. However, I've quickly checked the same test, just without a binding pattern:
// @module: commonjs
// @declaration: true
// @allowJs: true
// @types: node
// @currentDirectory: /
// @Filename: /node_modules/@types/node/index.d.ts
declare const require: any;
// @Filename: /a.js
module.exports = class Foo {}
// @Filename: /b.ts
const Foo = require("./a");
export default class extends Foo {}
And it behaves the same way - so at the very least this shouldn't be treated as a regression from this PR here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so this affected test case actually fails on the CI: https://github.com/microsoft/TypeScript/runs/6206598678?check_suite_focus=true
AssertionError: Program has no source file with name '/a.js'
I've gone to retest this on my machine and it's somewhat weird - I didn't see it when rerunning tests, this only starts to be reported AFTER I accept baselines. So when I've executed tests & just accepted baselines without rerunning stuff once again everything looked fine.
I've quickly set this situation up in a kitchen sink project and it seems that .d.ts
and .js
can be emitted for both a.js
and b.ts
just fine. Can this perhaps be an issue with the testing harness in this repo?
I've checked and those are the all file seen by the filesByName
:
/b.ts
/node_modules/@types/node/index.d.ts
/.ts/lib.d.ts
/.ts/lib.es5.d.ts
/.ts/lib.dom.d.ts
/.ts/lib.webworker.importscripts.d.ts
/.ts/lib.scripthost.d.ts
So indeed there is no source related to a.js
here 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really know what's going on here. If I go all the way back to #41257 and apply your change there, this still crashes. I don't really know what the deal is, how a source file could just not exist, or why this change would cause this to manifest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ye, this is super weird - I have a strong suspicion that this was some well-hidden bug in the testing harness because in the real project the filesByName
list is OK (it includes the a.js
file, IIRC).
I will get back to this in a couple of days and try to dig in more - if you have any suggestions or expectations about those involved cases I would really appreciate them. For some things I just lack context and it's harder for me to assess if the thing that I'm observing is correct or if it's a bug :p
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been trying to figure this one out; what I can see is that before your PR, no d.ts files are generated for declarationEmitExpressionInExtends6
, but after, it does. But, that d.ts
file is generated for a.js
, which is very weird.
Perhaps this has something to do with the fact that the code you've added is doing an unbounded findAncestor
, and the code surrounding it checks (weirdly, but I don't know the code) a specific set of parents only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure precisely, but if I go one commit before your change on your branch, the test harness doesn't hit this error because some dts.size is zero.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I dug into this deeper and I think I mostly got the situation correct.
.d.ts
should be generated in this situation because this test is usingallowJs: true
- however, the current test setup doesn't implement things exactly in the same way as the "real thing", it works on some assumptions for this case
- because of that the
.js
that should be treated as one of the input files is put intothis.otherFiles
- this wasn't a problem up until now because this test didn't emit any
.d.ts
because it contained a compilation error so a source file fora.js
was never requested (nor forb.ts
) - the compilation error was fixed by this PR so suddenly source files were started to be requested because, as you have mentioned,
d.ts
was started to be emitted here
I have no intention to fix the test harness here as that is out of the scope of this PR - so I've decided to rewrite this test to use import * as
+ destructuring as I think that in essence, it was the same as the require
-based test. It's worth highlighting that this import
-based test results in the same error on the current main
branch that the require
-based did. The error is gone though because the underlying issue was fixed by this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for looking at this harder. I agree that there seems to be something wrong with the test harness that needs to be fixed otherwise this'll trigger for unrelated reasons. Changing the test seems fine.
I'm not sure if we track bugs in the harness in actual issues, but maybe that's where we should put it as to not forget (with a reference back to this thread).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but I'd appreciate someone else checking that I didn't miss something here. (I was only waiting thanks to that odd harness error.)
@jakebailey who would be the most appropriate additional reviewer for this? |
@weswigham is listed as the other reviewer; he's definitely an appropriate reviewer. |
return addVisibleAlias(declaration, declaration.parent.parent.parent.parent); | ||
} | ||
else if (symbol.flags & SymbolFlags.BlockScopedVariable) { | ||
const variableStatement = findAncestor(declaration, isVariableStatement)!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My only remaining comment is the new branch's use of findAncestor
, as compared to the existing code's direct use of parents and specific checks; I'm not entirely certain that it matters but I do wonder how similar the two paths in the new isBindingElement
block are and if they should look the same and/or be sharing code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes sense here because binding patterns can be nested and thus I can't use specific checks like the other branches. The PR has tests covering those "recursive" scenarios.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, does this need to have some negative termination condition to prevent it from going too far, e.g. not go out to the wrong scope? Or is that impossible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that it's possible to go out too far here - I'm targeting the nearest variable statement that is guaranteed to be in the current scope. It's also not possible (I think) that this process statements from inner scopes as if it did then we'd have bugs in other branches of this code too.
Well, I'm confident enough that I'll just merge this. Thanks for working on this and waiting. |
Fixes #30598