Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -2254,7 +2254,7 @@ func GetDeclarationFromName(name *Node) *Declaration {
return nil
}
binExp := parent.Parent
if IsBinaryExpression(binExp) && GetAssignmentDeclarationKind(binExp.AsBinaryExpression()) != JSDeclarationKindNone {
if IsBinaryExpression(binExp) && GetAssignmentDeclarationKind(binExp) != JSDeclarationKindNone {
// (binExp.left as BindableStaticNameExpression).symbol || binExp.symbol
leftHasSymbol := false
if binExp.AsBinaryExpression().Left != nil && binExp.AsBinaryExpression().Left.Symbol() != nil {
Expand Down Expand Up @@ -6837,6 +6837,7 @@ func IsElementAccessExpression(node *Node) bool {

type CallExpression struct {
ExpressionBase
DeclarationBase
compositeNodeBase
Expression *Expression // Expression
QuestionDotToken *TokenNode // TokenNode
Expand Down Expand Up @@ -11169,7 +11170,7 @@ func (node *SourceFile) computeDeclarationMap() map[string][]*Node {
}
}
case KindBinaryExpression:
switch GetAssignmentDeclarationKind(node.AsBinaryExpression()) {
switch GetAssignmentDeclarationKind(node) {
case JSDeclarationKindThisProperty, JSDeclarationKindProperty:
addDeclaration(node)
}
Expand Down
120 changes: 72 additions & 48 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1394,15 +1394,16 @@ func GetNameOfDeclaration(declaration *Node) *Node {
func GetNonAssignedNameOfDeclaration(declaration *Node) *Node {
// !!!
switch declaration.Kind {
case KindBinaryExpression:
bin := declaration.AsBinaryExpression()
kind := GetAssignmentDeclarationKind(bin)
if kind == JSDeclarationKindProperty || kind == JSDeclarationKindThisProperty {
if name := GetElementOrPropertyAccessName(bin.Left); name != nil {
case KindBinaryExpression, KindCallExpression:
switch GetAssignmentDeclarationKind(declaration) {
case JSDeclarationKindProperty, JSDeclarationKindThisProperty, JSDeclarationKindExportsProperty:
left := declaration.AsBinaryExpression().Left
if name := GetElementOrPropertyAccessName(left); name != nil {
return name
} else {
return bin.Left
}
return left
case JSDeclarationKindObjectDefinePropertyValue, JSDeclarationKindObjectDefinePropertyExports:
return declaration.Arguments()[1]
}
return nil
case KindExportAssignment, KindJSExportAssignment:
Expand Down Expand Up @@ -1454,38 +1455,71 @@ type JSDeclarationKind int

const (
JSDeclarationKindNone JSDeclarationKind = iota
/// module.exports = expr
// module.exports = expr
JSDeclarationKindModuleExports
/// exports.name = expr
/// module.exports.name = expr
// exports.name = expr
// module.exports.name = expr
JSDeclarationKindExportsProperty
/// this.name = expr
// this.name = expr
JSDeclarationKindThisProperty
/// F.name = expr, F[name] = expr
// F.name = expr, F[name] = expr, in JS or TS file
JSDeclarationKindProperty
// Object.defineProperty(x, 'name', { value: any, writable?: boolean (false by default) });
// Object.defineProperty(x, 'name', { get: Function, set: Function });
// Object.defineProperty(x, 'name', { get: Function });
// Object.defineProperty(x, 'name', { set: Function });
JSDeclarationKindObjectDefinePropertyValue
// Object.defineProperty(exports || module.exports, 'name', ...);
JSDeclarationKindObjectDefinePropertyExports
)

func GetAssignmentDeclarationKind(bin *BinaryExpression) JSDeclarationKind {
if bin.OperatorToken.Kind != KindEqualsToken || !IsAccessExpression(bin.Left) {
return JSDeclarationKindNone
}
if IsInJSFile(bin.Left) && IsModuleExportsAccessExpression(bin.Left) {
return JSDeclarationKindModuleExports
} else if IsInJSFile(bin.Left) &&
(IsModuleExportsAccessExpression(bin.Left.Expression()) || IsExportsIdentifier(bin.Left.Expression())) &&
GetElementOrPropertyAccessName(bin.Left) != nil {
return JSDeclarationKindExportsProperty
}
if IsInJSFile(bin.Left) && bin.Left.Expression().Kind == KindThisKeyword {
return JSDeclarationKindThisProperty
}
if bin.Left.Kind == KindPropertyAccessExpression && IsEntityNameExpressionEx(bin.Left.Expression(), IsInJSFile(bin.Left)) && IsIdentifier(bin.Left.Name()) ||
bin.Left.Kind == KindElementAccessExpression && IsEntityNameExpressionEx(bin.Left.Expression(), IsInJSFile(bin.Left)) {
return JSDeclarationKindProperty
func GetAssignmentDeclarationKind(node *Node) JSDeclarationKind {
switch node.Kind {
case KindBinaryExpression:
bin := node.AsBinaryExpression()
if bin.OperatorToken.Kind == KindEqualsToken && IsAccessExpression(bin.Left) && !isVoidZero(GetRightMostAssignedExpression(bin.Right)) {
if IsInJSFile(bin.Left) {
if IsModuleExportsAccessExpression(bin.Left) {
return JSDeclarationKindModuleExports
}
if (IsModuleExportsAccessExpression(bin.Left.Expression()) || IsExportsIdentifier(bin.Left.Expression())) &&
GetElementOrPropertyAccessName(bin.Left) != nil {
return JSDeclarationKindExportsProperty
}
if bin.Left.Expression().Kind == KindThisKeyword {
return JSDeclarationKindThisProperty
}
}
if bin.Left.Kind == KindPropertyAccessExpression && IsEntityNameExpressionEx(bin.Left.Expression(), IsInJSFile(bin.Left)) && IsIdentifier(bin.Left.Name()) ||
bin.Left.Kind == KindElementAccessExpression && IsEntityNameExpressionEx(bin.Left.Expression(), IsInJSFile(bin.Left)) {
return JSDeclarationKindProperty
}
}
case KindCallExpression:
if IsInJSFile(node) && isBindableObjectDefinePropertyCall(node) {
entityName := node.Arguments()[0]
if IsExportsIdentifier(entityName) || IsModuleExportsAccessExpression(entityName) {
return JSDeclarationKindObjectDefinePropertyExports
}
return JSDeclarationKindObjectDefinePropertyValue
}
}
return JSDeclarationKindNone
}

func isBindableObjectDefinePropertyCall(node *Node) bool {
if args := node.Arguments(); len(args) == 3 {
if expr := node.Expression(); IsPropertyAccessExpression(expr) &&
IsIdentifier(expr.Expression()) && expr.Expression().Text() == "Object" &&
expr.Name().Text() == "defineProperty" &&
IsStringOrNumericLiteralLike(args[1]) &&
IsBindableStaticNameExpression(args[0] /*excludeThisKeyword*/, true) {
return true
}
}
return false
}

/**
* A declaration has a dynamic name if all of the following are true:
* 1. The declaration has a computed property name.
Expand Down Expand Up @@ -1517,31 +1551,17 @@ func IsEntityNameExpression(node *Node) bool {
}

func IsEntityNameExpressionEx(node *Node, allowJS bool) bool {
if node.Kind == KindIdentifier || IsPropertyAccessEntityNameExpression(node, allowJS) {
return true
}
if allowJS {
return node.Kind == KindThisKeyword || isElementAccessEntityNameExpression(node, allowJS)
}
return false
return IsIdentifier(node) ||
IsPropertyAccessEntityNameExpression(node, allowJS) ||
allowJS && (node.Kind == KindThisKeyword || isElementAccessEntityNameExpression(node, allowJS))
}

func IsPropertyAccessEntityNameExpression(node *Node, allowJS bool) bool {
if node.Kind == KindPropertyAccessExpression {
expr := node.AsPropertyAccessExpression()
return expr.Name().Kind == KindIdentifier && IsEntityNameExpressionEx(expr.Expression, allowJS)
}
return false
return IsPropertyAccessExpression(node) && node.Name().Kind == KindIdentifier && IsEntityNameExpressionEx(node.Expression(), allowJS)
}

func isElementAccessEntityNameExpression(node *Node, allowJS bool) bool {
if node.Kind == KindElementAccessExpression {
expr := node.AsElementAccessExpression()
if IsStringOrNumericLiteralLike(SkipParentheses(expr.ArgumentExpression)) {
return IsEntityNameExpressionEx(expr.Expression, allowJS)
}
}
return false
return IsElementAccessExpression(node) && IsStringOrNumericLiteralLike(node.AsElementAccessExpression().ArgumentExpression) && IsEntityNameExpressionEx(node.Expression(), allowJS)
}

func IsDottedName(node *Node) bool {
Expand Down Expand Up @@ -2747,6 +2767,10 @@ func IsModuleExportsAccessExpression(node *Node) bool {
return false
}

func IsModuleExportsQualifiedName(node *Node) bool {
return IsQualifiedName(node) && IsModuleIdentifier(node.AsQualifiedName().Left) && node.AsQualifiedName().Right.Text() == "exports"
}

func IsCheckJSEnabledForFile(sourceFile *SourceFile, compilerOptions *core.CompilerOptions) bool {
if sourceFile.CheckJsDirective != nil {
return sourceFile.CheckJsDirective.Enabled
Expand Down
37 changes: 29 additions & 8 deletions internal/binder/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,8 @@ func GetSymbolNameForPrivateIdentifier(containingClassSymbol *ast.Symbol, descri

func (b *Binder) declareModuleMember(node *ast.Node, symbolFlags ast.SymbolFlags, symbolExcludes ast.SymbolFlags) *ast.Symbol {
container := b.container
if ast.IsCommonJSExport(node) {
isCommonJSExport := ast.IsCommonJSExport(node) || ast.IsCallExpression(node)
if isCommonJSExport {
container = b.file.AsNode()
}
hasExportModifier := ast.GetCombinedModifierFlags(node)&ast.ModifierFlagsExport != 0 || ast.IsImplicitlyExportedJSTypeAlias(node)
Expand All @@ -401,8 +402,8 @@ func (b *Binder) declareModuleMember(node *ast.Node, symbolFlags ast.SymbolFlags
// during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation
// and this case is specially handled. Module augmentations should only be merged with original module definition
// and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed.
if !ast.IsAmbientModule(node) && (hasExportModifier || ast.IsCommonJSExport(node) || container.Flags&ast.NodeFlagsExportContext != 0) {
if !ast.IsLocalsContainer(container) || (ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) && b.getDeclarationName(node) == ast.InternalSymbolNameMissing) || ast.IsCommonJSExport(node) {
if !ast.IsAmbientModule(node) && (hasExportModifier || isCommonJSExport || container.Flags&ast.NodeFlagsExportContext != 0) {
if !ast.IsLocalsContainer(container) || (ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) && b.getDeclarationName(node) == ast.InternalSymbolNameMissing) || isCommonJSExport {
return b.declareSymbol(ast.GetExports(container.Symbol()), container.Symbol(), node, symbolFlags, symbolExcludes)
// No local symbol for an unnamed default!
}
Expand Down Expand Up @@ -620,7 +621,7 @@ func (b *Binder) bind(node *ast.Node) bool {
setFlowNode(node, b.currentFlow)
}
case ast.KindBinaryExpression:
switch ast.GetAssignmentDeclarationKind(node.AsBinaryExpression()) {
switch ast.GetAssignmentDeclarationKind(node) {
case ast.JSDeclarationKindProperty:
b.bindExpandoPropertyAssignment(node)
case ast.JSDeclarationKindThisProperty:
Expand Down Expand Up @@ -684,6 +685,12 @@ func (b *Binder) bind(node *ast.Node) bool {
case ast.KindInterfaceDeclaration:
b.bindBlockScopedDeclaration(node, ast.SymbolFlagsInterface, ast.SymbolFlagsInterfaceExcludes)
case ast.KindCallExpression:
switch ast.GetAssignmentDeclarationKind(node) {
case ast.JSDeclarationKindObjectDefinePropertyValue:
b.bindExpandoPropertyAssignment(node)
case ast.JSDeclarationKindObjectDefinePropertyExports:
b.bindObjectDefinePropertyExport(node)
}
if ast.IsInJSFile(node) {
b.bindCallExpression(node)
}
Expand Down Expand Up @@ -1004,8 +1011,7 @@ func addLateBoundAssignmentDeclarationToSymbol(node *ast.Node, symbol *ast.Symbo
}

func (b *Binder) bindExpandoPropertyAssignment(node *ast.Node) {
expr := node.AsBinaryExpression()
parent := expr.Left.Expression()
parent := getParentOfPropertyAssignment(node)
symbol := b.lookupEntity(parent, b.blockScopeContainer)
if symbol == nil {
symbol = b.lookupEntity(parent, b.container)
Expand All @@ -1020,6 +1026,22 @@ func (b *Binder) bindExpandoPropertyAssignment(node *ast.Node) {
}
}

func getParentOfPropertyAssignment(node *ast.Node) *ast.Node {
switch node.Kind {
case ast.KindBinaryExpression:
return node.AsBinaryExpression().Left.Expression()
case ast.KindCallExpression:
return node.Arguments()[0]
}
panic("Unhandled case in getParentOfPropertyAssignment")
}

func (b *Binder) bindObjectDefinePropertyExport(node *ast.Node) {
if b.setCommonJSModuleIndicator(node) {
b.declareModuleMember(node, ast.SymbolFlagsFunctionScopedVariable, ast.SymbolFlagsFunctionScopedVariableExcludes)
}
}

func getInitializerSymbol(symbol *ast.Symbol) *ast.Symbol {
if symbol == nil || symbol.ValueDeclaration == nil {
return nil
Expand Down Expand Up @@ -1205,7 +1227,7 @@ func (b *Binder) lookupEntity(node *ast.Node, container *ast.Node) *ast.Symbol {
if ast.IsIdentifier(node) {
return b.lookupName(node.Text(), container)
}
if (ast.IsPropertyAccessExpression(node) || ast.IsElementAccessExpression(node)) && node.Expression().Kind == ast.KindThisKeyword {
if node.Expression().Kind == ast.KindThisKeyword {
if _, symbolTable := b.getThisClassAndSymbolTable(); symbolTable != nil {
if name := ast.GetElementOrPropertyAccessName(node); name != nil {
return symbolTable[name.Text()]
Expand All @@ -1227,7 +1249,6 @@ func (b *Binder) lookupName(name string, container *ast.Node) *ast.Symbol {
return core.OrElse(local.ExportSymbol, local)
}
}

if declaration := container.DeclarationData(); declaration != nil && declaration.Symbol != nil {
return declaration.Symbol.Exports[name]
}
Expand Down
2 changes: 1 addition & 1 deletion internal/binder/nameresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ loop:
lastLocation.AsSourceFile().CommonJSModuleIndicator != nil &&
name == "module" &&
originalLocation.Parent != nil &&
ast.IsModuleExportsAccessExpression(originalLocation.Parent) &&
(ast.IsModuleExportsAccessExpression(originalLocation.Parent) || ast.IsModuleExportsQualifiedName(originalLocation.Parent)) &&
meaning&lastLocation.Symbol().Flags != 0 {
return r.GetModuleSymbol(lastLocation)
}
Expand Down
Loading
Loading