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

Incorrect namespaces in externs when non-ambient declarations are imported #1202

Open
theseanl opened this issue Sep 30, 2020 · 5 comments
Open

Comments

@theseanl
Copy link
Contributor

theseanl commented Sep 30, 2020

When non-ambient declarations in a module .d.ts file is imported and referenced by another .d.ts, it will have an incorrect name.

Here is a repro: https://github.com/theseanl/tsickle-ambient-externs-bug: execute node invoke_tsickle.js to generate externs.js, then its line 46 contains an identifier that is nowhere defined.

@mprobst
Copy link
Contributor

mprobst commented Sep 30, 2020 via email

@theseanl
Copy link
Contributor Author

theseanl commented Sep 30, 2020

By "ambient" I was just referring to the fact that isAmbient is returning true. In that file, only the declarations marked with declare keyword is treated as ambient.

@theseanl
Copy link
Contributor Author

theseanl commented Sep 30, 2020

@mprobst Maybe then the isAmbient function is not returning what is intended?

It seems that SourceFile nodes do not contian the ts.ModifierFlags.Ambient modifier, in fact on my end it seems to have none, so if a node is not explicitly marked with declare keyword, isAmbient will return false for such a node.

In view of this, it seems to me that we don't need a conditional clause here https://github.com/angular/tsickle/blob/master/src/externs.ts#L629 that checks whether a node is ambient or not. If it belongs to a .d.ts file, then everything is "ambient", and otherwise we are checking ambient-ness at https://github.com/angular/tsickle/blob/master/src/externs.ts#L208 .

Moreover, it is invalid to use a value returned from host.pathToModuleName directly as an alias name here at https://github.com/angular/tsickle/blob/master/src/externs.ts#L632, because types of module names are different from identifiers and I guess that's why there is a moduleNameAsIdentifier function.

Thus removing isAmbientModuleDeclaration check and leaving only the branch with the moduleNameAsIdentifier call looks like what should be done and it fixes the issue on my end.

@mprobst
Copy link
Contributor

mprobst commented Sep 30, 2020 via email

@theseanl
Copy link
Contributor Author

Hm, I'm not sure if I understand the situation you've describe correctly. I've tried to look up for a similar test case but couldn't find one -- could you elaborate?

So, there is a goog.module

goog.module('a.b.c'); // <---- module name produced by host.pathToModuleName

function A() { };
/** @type {number} */
A.prototype.prop;

or something, which contains a regular code which defines a class, for example.
And this is referenced in an externs file like this

/** @externs */
/**
 * @param {a.b.c.A} x  <------- this is what the non-ambient branch produces, using host.pathToModuleName
 * @return {*}
 */
some_function = function(x) { }

Does this actually work in closure compiler? I thought one needs goog.declareLegacyNamespace for something like this to work, which tsickle does not generate.

In addition, if someone imports a non-ambient type declaration (that is, without declare keyword) in a .d.ts file, then it will not be referred as goog.module.path.plus.local.name and instead it will always be ?, because of this line

if (this.isForExterns && isModule && !isAmbient) return '?';

If the type declaration was ambient then it will be included in externs and there will be alias names, so there is no issue.

These considerations make me inclined to think that there is no case that is relevant to the described situation, but this could be wrong. When I simply change lines

tsickle/src/externs.ts

Lines 632 to 633 in 0a0b1b5

aliasName = host.pathToModuleName(
sourceFile.fileName, resolveModuleName(host, sourceFile.fileName, fullUri));

to

aliasName = moduleNameAsIdentifier(host, resolveModuleName(host, sourceFile.fileName, fullUri));

(I haven't check that this does not do the same operation twice, but it gets the right result)
then e2e_test, golden_test, unit_test still passes. I can't locally check closure_e2e_test because it was anyway failing with an error

error message
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.protobuf.UnsafeUtil (file:...) to field java.nio.Buffer.address
WARNING: Please consider reporting this to the maintainers of com.google.protobuf.UnsafeUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants