Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
124 changes: 74 additions & 50 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func IsAssignmentExpression(node *Node, excludeCompoundAssignment bool) bool {
}

func GetRightMostAssignedExpression(node *Node) *Node {
for IsAssignmentExpression(node, true /*excludeCompoundAssignment*/) {
for IsAssignmentExpression(node, false /*excludeCompoundAssignment*/) {
node = node.AsBinaryExpression().Right
}
return node
Expand Down Expand Up @@ -1286,7 +1286,7 @@ func IsImportOrExportSpecifier(node *Node) bool {
return IsImportSpecifier(node) || IsExportSpecifier(node)
}

func isVoidZero(node *Node) bool {
func IsVoidZero(node *Node) bool {
return IsVoidExpression(node) && IsNumericLiteral(node.Expression()) && node.Expression().Text() == "0"
}

Expand Down 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) {
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