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

Serialize (noncontextual) keyword named namespace members with export declarations in both declaration emitters #38982

Merged
merged 4 commits into from
Jun 10, 2020

Conversation

weswigham
Copy link
Member

Fixes #38750

cc @a-tarasyuk I pulled in your test from #38874; I went with a slightly different style of emit for the normal declaration emitter, and, well, the js declaration emitter was just another beast entirely (note for reviewers: I inlined one of the helper functions in the js declaration emitter, so the change isn't actually that large; I just had to inline the helper to set a condition in a branch of it).

Note that the OP in #38750 was hoping to also have non-identifier strings like "foo bar" also roundtrip; unfortunately, with this emit scheme (as @DanielRosenwasser proposed), such a thing is not possible (without amending syntax somewhere anyway).

@@ -139,7 +139,7 @@ export namespace f {
* @template T
* @param {T} a
*/
export function self<T>(a: T): T;
function self<T>(a: T): T;
Copy link
Member Author

Choose a reason for hiding this comment

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

While working on this, I found a need for the js declaration emitter to automatically add/strip export modifiers from nested namespace members as needed; so the result of this can be seen in some baseline updates like this - since every member of the containing namespace was exported, there was no need to explicitly have the export modifiers. Now they get stripped, consistent with our transform-based declaration emitter.

@@ -73,7 +73,7 @@ declare class B3 implements A {
}
declare namespace Ns {
export { C1 };
const C5: {
export const C5: {
Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this was mistakenly missing an export modifier before. (The export statement above made it required, but the emitter never added it).

@@ -285,7 +285,7 @@ module.exports = x;
},
"./obj.json": {
"version": "2151907832-{\n \"val\": 42\n}",
"signature": "-6323167306-export declare const val: number;\r\n",
"signature": "-5546159834-export const val: number;\r\n",
Copy link
Member Author

Choose a reason for hiding this comment

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

So! Fun fact: We parse json documents essentially as export = { val: 42 }. Previous to this change, we'd serialize it to

declare namespace export_ {
    export declare const val: number;
}
export = _export;

then inline it to

export declare const val: number;

Now, we first make

declare namespace export_ {
    const val: number;
}
export = _export;

since we can omit the export and declare modifiers, and inline to

const val: number;

which we then patch an export modifier back onto (since we're inlining a namespace where everything was implicitly exported):

export const val: number;

so our frugality with the export modifier has, in this case, also caused us to be more frugal with the declare modifier, too. This should be fine, since everything in a .d.ts file is already implicitly declare'd anyway.

Copy link
Member

@sandersn sandersn left a comment

Choose a reason for hiding this comment

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

The fix looks right. I have a few suggestions and one small question before you merge.

@@ -0,0 +1,84 @@
// @declaration: true
Copy link
Member

Choose a reason for hiding this comment

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

nitpick: shouldn't this test be named tsDeclarationsFunctionKeywordPropExhaustive to match the js test?

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 pulled this in from @a-tarasyuk 's PR and wanted to preserve his history.

declare namespace foo {
export var x: number;
export var y: number;
var _a: number;
Copy link
Member

Choose a reason for hiding this comment

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

wwhy doesn't the ts version generate good names like _break? I guess it's because the JS declaration emit is purpose-built for this, but the TS code is pre-existing.

Copy link
Member Author

Choose a reason for hiding this comment

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

Pretty much that. The TS code is just using getGeneratedNameForNode (which is simple and uses these alphanumeric names), while the JS code has a lot of scope tracking machinery in getInternalSymbolName to generate better names where possible.

/*modifiers*/ undefined,
createNamedExports([createExportSpecifier(d.expression, createIdentifier(InternalSymbolName.Default))])
) : d);
const exportModifierStripped = every(defaultReplaced, d => hasSyntacticModifier(d, ModifierFlags.Export)) ? map(defaultReplaced, removeExportModifier) : defaultReplaced;
Copy link
Member

Choose a reason for hiding this comment

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

Don't we have a mapSame that would make this more succnctly expressed as

Suggested change
const exportModifierStripped = every(defaultReplaced, d => hasSyntacticModifier(d, ModifierFlags.Export)) ? map(defaultReplaced, removeExportModifier) : defaultReplaced;
const exportModifierStripped = mapSame(defaultReplaced, d => hasSyntacticModifier(d, ModifierFlags.Export) ? removeExportModifier(d) : d);

Copy link
Member Author

Choose a reason for hiding this comment

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

Nope, that's not the same at all 😄

If and only if every export has an export modifier, then we can strip the export modifier from every export and retain the same meaning. With your suggestion, we'd remove export modifiers from everything, even if some things didn't have export modifiers, which is very wrong and would make privates public.

@@ -5936,6 +5939,13 @@ namespace ts {
statement.modifierFlagsCache = 0;
}

function removeExportModifier(statement: Statement) {
Copy link
Member

Choose a reason for hiding this comment

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

could this be defined closer to its usage?

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 prefer it to be here, next to it's cousin, addExportModifier, whose implementation is nearly identical.

@weswigham weswigham merged commit 08cb0b2 into microsoft:master Jun 10, 2020
@weswigham weswigham deleted the keyword-named-function-props branch June 10, 2020 21:42
cangSDARM added a commit to cangSDARM/TypeScript that referenced this pull request Jun 15, 2020
* upstream/master: (22 commits)
  Small fix in `getIsContextSensitiveAssignmentOrContextType`
  Simplify `visitObjectLiteralExpression`
  Fix handling of `aruments` in the emitter
  Fix declaration emit for property references of imported object literal types (microsoft#39055)
  Fix casing for wild card keys for implicit globs to get wild card directories to watch (microsoft#39049)
  DOM update 2020-06-12
  fix(a11y): make ISSUE_TEMPLATE/Bug_report.md more accessible for folks with screen readers (microsoft#39013)
  Update request-pr-review script to latest version of octokit (microsoft#39031)
  pin version of octokit
  skip implements types with no symbols
  isDynamicName skips parentheses for element access
  Allow `e: unknown` in `catch` arguments
  Serialize (noncontextual) keyword named namespace members with export declarations in both declaration emitters (microsoft#38982)
  Patch to use this.timeout() > 0 rather than this.enableTimeout() to work with mocha 8+
  Handle missing return type nodes and nested type references missing type arguments in existing jsdoc node serialization (microsoft#39011)
  Add containerName to CallHierarchyItem (microsoft#38997)
  Fix isSameEntityName (microsoft#38999)
  Fix typo for 'blocklist' (microsoft#39001)
  remove errant tab
  Switch to isSymbolAccessible for both.
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Adding keyword-named properties to functions generates invalid declarations
4 participants