Skip to content

Commit 698d40d

Browse files
committed
fix(1374): support declaration emit for expando functions
1 parent 2b82831 commit 698d40d

File tree

51 files changed

+1140
-263
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1140
-263
lines changed

internal/ast/utilities.go

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,7 +1348,7 @@ func IsBindableStaticElementAccessExpression(node *Node, excludeThisKeyword bool
13481348
return IsLiteralLikeElementAccess(node) &&
13491349
((!excludeThisKeyword && node.Expression().Kind == KindThisKeyword) ||
13501350
IsEntityNameExpression(node.Expression()) ||
1351-
IsBindableStaticAccessExpression(node.Expression() /*excludeThisKeyword*/, true))
1351+
IsBindableStaticAccessExpression(node.Expression(), true /*excludeThisKeyword*/))
13521352
}
13531353

13541354
func IsLiteralLikeElementAccess(node *Node) bool {
@@ -2817,10 +2817,6 @@ func IsModuleExportsAccessExpression(node *Node) bool {
28172817
return false
28182818
}
28192819

2820-
func isLiteralLikeElementAccess(node *Node) bool {
2821-
return node.Kind == KindElementAccessExpression && IsStringOrNumericLiteralLike(node.AsElementAccessExpression().ArgumentExpression)
2822-
}
2823-
28242820
func IsCheckJSEnabledForFile(sourceFile *SourceFile, compilerOptions *core.CompilerOptions) bool {
28252821
if sourceFile.CheckJsDirective != nil {
28262822
return sourceFile.CheckJsDirective.Enabled
@@ -2924,6 +2920,14 @@ func IsContextualKeyword(token Kind) bool {
29242920
return KindFirstContextualKeyword <= token && token <= KindLastContextualKeyword
29252921
}
29262922

2923+
func IsKeyword(token Kind) bool {
2924+
return KindFirstKeyword <= token && token <= KindLastKeyword
2925+
}
2926+
2927+
func IsNonContextualKeyword(token Kind) bool {
2928+
return IsKeyword(token) && !IsContextualKeyword(token)
2929+
}
2930+
29272931
func IsThisInTypeQuery(node *Node) bool {
29282932
if !IsThisIdentifier(node) {
29292933
return false
@@ -3633,3 +3637,79 @@ func GetSemanticJsxChildren(children []*JsxChild) []*JsxChild {
36333637
}
36343638
})
36353639
}
3640+
3641+
func IsExpandoPropertyDeclaration(node *Node) bool {
3642+
if node == nil {
3643+
return false
3644+
}
3645+
return IsPropertyAccessExpression(node) || IsElementAccessExpression(node) || IsBinaryExpression(node)
3646+
}
3647+
3648+
func GetExpandoInitializer(initializer *Node, isPrototypeAssignment bool) *Node {
3649+
if initializer.Kind == KindCallExpression {
3650+
expr := SkipParentheses(initializer.Expression())
3651+
if expr.Kind == KindFunctionExpression || expr.Kind == KindArrowFunction {
3652+
return initializer
3653+
}
3654+
return nil
3655+
}
3656+
3657+
if initializer.Kind == KindFunctionExpression || initializer.Kind == KindCallExpression || initializer.Kind == KindArrowFunction {
3658+
return initializer
3659+
}
3660+
3661+
if initializer.Kind == KindObjectLiteralExpression && (len(initializer.Properties()) == 0 || isPrototypeAssignment) {
3662+
return initializer
3663+
}
3664+
3665+
return nil
3666+
}
3667+
3668+
func GetEffectiveInitializer(node *Node) *Expression {
3669+
if IsInJSFile(node) && node.Initializer() != nil && IsBinaryExpression(node.Initializer()) {
3670+
initializer := node.Initializer().AsBinaryExpression()
3671+
if initializer.OperatorToken.Kind == KindBarBarToken || initializer.OperatorToken.Kind == KindQuestionQuestionToken {
3672+
if node.Name() != nil && IsEntityNameExpressionEx(node.Name(), IsInJSFile(node)) && IsSameEntityName(node.Name(), initializer.Left) {
3673+
return initializer.Right
3674+
}
3675+
}
3676+
}
3677+
return node.Initializer()
3678+
}
3679+
3680+
func GetDeclaredExpandoInitializer(node *Node) *Expression {
3681+
initializer := GetEffectiveInitializer(node)
3682+
if initializer == nil {
3683+
return nil
3684+
}
3685+
return GetExpandoInitializer(initializer, IsPrototypeAccess(node.Name()))
3686+
}
3687+
3688+
func IsPrototypeAccess(node *Node) bool {
3689+
return IsBindableStaticAccessExpression(node, false /*excludeThisKeyword*/)
3690+
}
3691+
3692+
func IsLiteralLikeAccess(node *Node) bool {
3693+
return IsPropertyAccessExpression(node) || IsLiteralLikeElementAccess(node)
3694+
}
3695+
3696+
func GetNameOrArgument(node *Expression) *Expression {
3697+
if IsPropertyAccessExpression(node) {
3698+
return node.Name()
3699+
}
3700+
return node.AsElementAccessExpression().ArgumentExpression
3701+
}
3702+
3703+
func IsSameEntityName(name *Expression, initializer *Expression) bool {
3704+
if IsPropertyNameLiteral(name) && IsPropertyNameLiteral(initializer) {
3705+
return name.Text() == initializer.Text()
3706+
}
3707+
if IsMemberName(name) && IsLiteralLikeAccess(initializer) && (initializer.Expression().Kind == KindThisKeyword || IsIdentifier(initializer.Expression()) &&
3708+
(initializer.Expression().Text() == "window" || initializer.Expression().Text() == "self" || initializer.Expression().Text() == "global")) {
3709+
return IsSameEntityName(name, GetNameOrArgument(initializer))
3710+
}
3711+
if IsLiteralLikeAccess(name) && IsLiteralLikeAccess(initializer) {
3712+
return GetElementOrPropertyAccessName(name) == GetElementOrPropertyAccessName(initializer) && IsSameEntityName(name.Expression(), initializer.Expression())
3713+
}
3714+
return false
3715+
}

internal/checker/emitresolver.go

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -605,8 +605,40 @@ func (r *emitResolver) IsLiteralConstDeclaration(node *ast.Node) bool {
605605
}
606606

607607
func (r *emitResolver) IsExpandoFunctionDeclaration(node *ast.Node) bool {
608-
// node = r.emitContext.ParseNode(node)
609-
// !!! TODO: expando function support
608+
if !ast.IsParseTreeNode(node) {
609+
return false
610+
}
611+
612+
var symbol *ast.Symbol
613+
if ast.IsVariableDeclaration(node) {
614+
if node.Type() != nil || (!ast.IsInJSFile(node) && !ast.IsVarConstLike(node)) {
615+
return false
616+
}
617+
initializer := ast.GetDeclaredExpandoInitializer(node)
618+
if initializer == nil || !ast.CanHaveSymbol(initializer) {
619+
return false
620+
}
621+
symbol = r.checker.getSymbolOfDeclaration(initializer)
622+
} else if ast.IsFunctionDeclaration(node) {
623+
symbol = r.checker.getSymbolOfDeclaration(node)
624+
}
625+
626+
if symbol == nil || (symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsVariable)) == 0 {
627+
return false
628+
}
629+
630+
exports := r.checker.getExportsOfSymbol(symbol)
631+
for _, p := range exports {
632+
if p.Flags&ast.SymbolFlagsValue == 0 {
633+
continue
634+
}
635+
if p.ValueDeclaration == nil {
636+
continue
637+
}
638+
if ast.IsExpandoPropertyDeclaration(p.ValueDeclaration) {
639+
return true
640+
}
641+
}
610642
return false
611643
}
612644

@@ -847,6 +879,23 @@ func (r *emitResolver) GetReferencedValueDeclarations(node *ast.IdentifierNode)
847879
return r.getReferenceResolver().GetReferencedValueDeclarations(node)
848880
}
849881

882+
func (r *emitResolver) GetPropertiesOfContainerFunction(node *ast.Node) []*ast.Symbol {
883+
props := []*ast.Symbol{}
884+
885+
if !ast.IsParseTreeNode(node) {
886+
return props
887+
}
888+
889+
if ast.IsFunctionDeclaration(node) {
890+
symbol := r.checker.getSymbolOfDeclaration(node)
891+
if symbol == nil {
892+
return props
893+
}
894+
props = r.checker.getPropertiesOfType(r.checker.getTypeOfSymbol(symbol))
895+
}
896+
return props
897+
}
898+
850899
// TODO: the emit resolver being responsible for some amount of node construction is a very leaky abstraction,
851900
// and requires giving it access to a lot of context it's otherwise not required to have, which also further complicates the API
852901
// and likely reduces performance. There's probably some refactoring that could be done here to simplify this.

internal/parser/parser.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ func (p *Parser) lookAhead(callback func(p *Parser) bool) bool {
293293

294294
func (p *Parser) nextToken() ast.Kind {
295295
// if the keyword had an escape
296-
if isKeyword(p.token) && (p.scanner.HasUnicodeEscape() || p.scanner.HasExtendedUnicodeEscape()) {
296+
if ast.IsKeyword(p.token) && (p.scanner.HasUnicodeEscape() || p.scanner.HasExtendedUnicodeEscape()) {
297297
// issue a parse error for the escape
298298
p.parseErrorAtCurrentToken(diagnostics.Keywords_cannot_contain_escape_characters)
299299
}
@@ -641,7 +641,7 @@ func (p *Parser) parsingContextErrors(context ParsingContext) {
641641
case PCHeritageClauseElement:
642642
p.parseErrorAtCurrentToken(diagnostics.Expression_expected)
643643
case PCVariableDeclarations:
644-
if isKeyword(p.token) {
644+
if ast.IsKeyword(p.token) {
645645
p.parseErrorAtCurrentToken(diagnostics.X_0_is_not_allowed_as_a_variable_declaration_name, scanner.TokenToString(p.token))
646646
} else {
647647
p.parseErrorAtCurrentToken(diagnostics.Variable_declaration_expected)
@@ -659,7 +659,7 @@ func (p *Parser) parsingContextErrors(context ParsingContext) {
659659
case PCJSDocParameters:
660660
p.parseErrorAtCurrentToken(diagnostics.Parameter_declaration_expected)
661661
case PCParameters:
662-
if isKeyword(p.token) {
662+
if ast.IsKeyword(p.token) {
663663
p.parseErrorAtCurrentToken(diagnostics.X_0_is_not_allowed_as_a_parameter_name, scanner.TokenToString(p.token))
664664
} else {
665665
p.parseErrorAtCurrentToken(diagnostics.Parameter_declaration_expected)
@@ -2376,7 +2376,7 @@ func (p *Parser) parseModuleExportName(disallowKeywords bool) (node *ast.Node, n
23762376
if p.token == ast.KindStringLiteral {
23772377
return p.parseLiteralExpression(false /*intern*/), nameOk
23782378
}
2379-
if disallowKeywords && isKeyword(p.token) && !p.isIdentifier() {
2379+
if disallowKeywords && ast.IsKeyword(p.token) && !p.isIdentifier() {
23802380
nameOk = false
23812381
}
23822382
return p.parseIdentifierName(), nameOk
@@ -6011,7 +6011,7 @@ func (p *Parser) scanClassMemberStart() bool {
60116011
// If we were able to get any potential identifier...
60126012
if idToken != ast.KindUnknown {
60136013
// If we have a non-keyword identifier, or if we have an accessor, then it's safe to parse.
6014-
if !isKeyword(idToken) || idToken == ast.KindSetKeyword || idToken == ast.KindGetKeyword {
6014+
if !ast.IsKeyword(idToken) || idToken == ast.KindSetKeyword || idToken == ast.KindGetKeyword {
60156015
return true
60166016
}
60176017
// If it *is* a keyword, but not an accessor, check a little farther along
@@ -6411,10 +6411,6 @@ func (p *Parser) skipRangeTrivia(textRange core.TextRange) core.TextRange {
64116411
return core.NewTextRange(scanner.SkipTrivia(p.sourceText, textRange.Pos()), textRange.End())
64126412
}
64136413

6414-
func isKeyword(token ast.Kind) bool {
6415-
return ast.KindFirstKeyword <= token && token <= ast.KindLastKeyword
6416-
}
6417-
64186414
func isReservedWord(token ast.Kind) bool {
64196415
return ast.KindFirstReservedWord <= token && token <= ast.KindLastReservedWord
64206416
}

internal/printer/emitresolver.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type EmitResolver interface {
3434
GetExternalModuleFileFromDeclaration(node *ast.Node) *ast.SourceFile
3535
GetEffectiveDeclarationFlags(node *ast.Node, flags ast.ModifierFlags) ast.ModifierFlags
3636
GetResolutionModeOverride(node *ast.Node) core.ResolutionMode
37+
GetPropertiesOfContainerFunction(node *ast.Node) []*ast.Symbol
3738

3839
// JSX Emit
3940
GetJsxFactoryEntity(location *ast.Node) *ast.Node

0 commit comments

Comments
 (0)