Skip to content

Fix emit for leading 'var' declarations for synthesized namespaces #17631

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

Merged
merged 12 commits into from
Aug 17, 2017

Conversation

DanielRosenwasser
Copy link
Member

@DanielRosenwasser DanielRosenwasser commented Aug 5, 2017

Down-leveling a namespace to ES involved an optimization within individual files to avoid emitting multiple var declarations by grabbing the namespace's symbol's name and checking whether a prior symbol with a given name already had a var declaration emitted. However, the check was not conservative enough to account for synthetic nodes that don't have symbols.

With this PR, we unconditionally emit the var declaration for namespaces that have no symbols.

With this PR, we just use local identifiers to keep track of declarations in the current scope.

Fixes #17596.

Copy link
Contributor

@rbuckton rbuckton left a comment

Choose a reason for hiding this comment

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

After another review it seems this is not a complete solution as it could be possible to end up with an error at runtime if you merge a class and a namespace, where you synthesize an updated namespace during a transformation:

class C { }
namespace C { ... }

If you replace namespace C with an updated node, then the ES2015 emit might end up as:

class C { }
var C; // error: cannot redeclare block-scoped binding.
(function (C) { ... })(C || (C = {}));

function isFirstEmittedDeclarationInScope(node: Node) {
function isPotentiallyFirstEmittedDeclarationInScope(node: Node) {
// If the node has a named symbol, then we have enough knowledge to determine
// whether a prior declaration has been emitted.
Copy link
Contributor

Choose a reason for hiding this comment

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

We should consider using getParseTreeNode here and get the symbol of the parse tree node. If there is no parse tree node, or the node does not have a symbol, we should fall back to the text of the declaration's name.

@DanielRosenwasser
Copy link
Member Author

DanielRosenwasser commented Aug 15, 2017

I've changed the implementation to just never use symbols; we have escapedName anyway, and this is probably better than tracking down the original symbols, since a prior transform could have changed the name anyhow.

Copy link
Contributor

@rbuckton rbuckton left a comment

Choose a reason for hiding this comment

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

A few minor comments, but looks good otherwise.

}
}

/**
* Determines whether a declaration is the first declaration with the same name emitted
* in the current scope.
* Determines whether a declaration is *could* be the first declaration with
Copy link
Contributor

Choose a reason for hiding this comment

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

a declaration is *could*

}

function declaredNameInScope(node: FunctionDeclaration | ClassDeclaration | ModuleDeclaration | EnumDeclaration): __String {
if (node.name.kind !== SyntaxKind.Identifier) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Debug.assertNode(node.name, isIdentifier)

@@ -2746,7 +2760,7 @@ namespace ts {
return createNotEmittedStatement(node);
}

Debug.assert(isIdentifier(node.name), "TypeScript module should have an Identifier name.");
Debug.assert(isIdentifier(node.name), "A TypeScript namespace should have an Identifier name.");
Copy link
Contributor

Choose a reason for hiding this comment

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

Debug.assertNode(node, isIdentifier)

@DanielRosenwasser DanielRosenwasser merged commit 2729281 into master Aug 17, 2017
@DanielRosenwasser DanielRosenwasser changed the title Always emit leading 'var' declarations for synthesized namespaces Fix emit for leading 'var' declarations for synthesized namespaces Aug 17, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants