Skip to content
Open
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/microsoft/typescript-go

go 1.25
go 1.25.1

require (
github.com/dlclark/regexp2 v1.11.5
Expand Down
31 changes: 26 additions & 5 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1340,7 +1340,7 @@ func IsBindableStaticElementAccessExpression(node *Node, excludeThisKeyword bool
return IsLiteralLikeElementAccess(node) &&
((!excludeThisKeyword && node.Expression().Kind == KindThisKeyword) ||
IsEntityNameExpression(node.Expression()) ||
IsBindableStaticAccessExpression(node.Expression() /*excludeThisKeyword*/, true))
IsBindableStaticAccessExpression(node.Expression(), true /*excludeThisKeyword*/))
}

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

func isLiteralLikeElementAccess(node *Node) bool {
return node.Kind == KindElementAccessExpression && IsStringOrNumericLiteralLike(node.AsElementAccessExpression().ArgumentExpression)
}

func IsCheckJSEnabledForFile(sourceFile *SourceFile, compilerOptions *core.CompilerOptions) bool {
if sourceFile.CheckJsDirective != nil {
return sourceFile.CheckJsDirective.Enabled
Expand Down Expand Up @@ -3866,6 +3862,31 @@ func IsImportOrImportEqualsDeclaration(node *Node) bool {
return IsImportDeclaration(node) || IsImportEqualsDeclaration(node)
}

func IsKeyword(token Kind) bool {
return KindFirstKeyword <= token && token <= KindLastKeyword
}

func IsNonContextualKeyword(token Kind) bool {
return IsKeyword(token) && !IsContextualKeyword(token)
}

func HasModifier(node *Node, flags ModifierFlags) bool {
return node.ModifierFlags()&flags != 0
}

func IsExpandoInitializer(initializer *Node) bool {
if initializer == nil {
return false
}
if IsFunctionExpressionOrArrowFunction(initializer) {
return true
}
if IsInJSFile(initializer) {
return IsClassExpression(initializer) || (IsObjectLiteralExpression(initializer) && len(initializer.AsObjectLiteralExpression().Properties.Nodes) == 0)
}
return false
}

func GetContainingFunction(node *Node) *Node {
return FindAncestor(node.Parent, IsFunctionLike)
}
16 changes: 2 additions & 14 deletions internal/binder/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1018,30 +1018,18 @@ func getInitializerSymbol(symbol *ast.Symbol) *ast.Symbol {
case ast.IsVariableDeclaration(declaration) &&
(declaration.Parent.Flags&ast.NodeFlagsConst != 0 || ast.IsInJSFile(declaration)):
initializer := declaration.Initializer()
if isExpandoInitializer(initializer) {
if ast.IsExpandoInitializer(initializer) {
return initializer.Symbol()
}
case ast.IsBinaryExpression(declaration) && ast.IsInJSFile(declaration):
initializer := declaration.AsBinaryExpression().Right
if isExpandoInitializer(initializer) {
if ast.IsExpandoInitializer(initializer) {
return initializer.Symbol()
}
}
return nil
}

func isExpandoInitializer(initializer *ast.Node) bool {
if initializer == nil {
return false
}
if ast.IsFunctionExpressionOrArrowFunction(initializer) {
return true
} else if ast.IsInJSFile(initializer) {
return ast.IsClassExpression(initializer) || (ast.IsObjectLiteralExpression(initializer) && len(initializer.AsObjectLiteralExpression().Properties.Nodes) == 0)
}
return false
}

func (b *Binder) bindThisPropertyAssignment(node *ast.Node) {
if !ast.IsInJSFile(node) {
return
Expand Down
13 changes: 13 additions & 0 deletions internal/binder/referenceresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type ReferenceResolver interface {
GetReferencedImportDeclaration(node *ast.IdentifierNode) *ast.Declaration
GetReferencedValueDeclaration(node *ast.IdentifierNode) *ast.Declaration
GetReferencedValueDeclarations(node *ast.IdentifierNode) []*ast.Declaration
GetElementAccessExpressionName(expression *ast.ElementAccessExpression) string
}

type ReferenceResolverHooks struct {
Expand All @@ -21,6 +22,7 @@ type ReferenceResolverHooks struct {
GetSymbolOfDeclaration func(*ast.Declaration) *ast.Symbol
GetTypeOnlyAliasDeclaration func(symbol *ast.Symbol, include ast.SymbolFlags) *ast.Declaration
GetExportSymbolOfValueSymbolIfExported func(*ast.Symbol) *ast.Symbol
GetElementAccessExpressionName func(*ast.ElementAccessExpression) (string, bool)
}

var _ ReferenceResolver = &referenceResolver{}
Expand Down Expand Up @@ -236,3 +238,14 @@ func (r *referenceResolver) GetReferencedValueDeclarations(node *ast.IdentifierN
}
return declarations
}

func (r *referenceResolver) GetElementAccessExpressionName(expression *ast.ElementAccessExpression) string {
if expression != nil {
if r.hooks.GetElementAccessExpressionName != nil {
if name, ok := r.hooks.GetElementAccessExpressionName(expression); ok {
return name
}
}
}
return ""
}
96 changes: 58 additions & 38 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -1905,46 +1905,64 @@ func (c *Checker) isBlockScopedNameDeclaredBeforeUse(declaration *ast.Node, usag
}

func (c *Checker) isUsedInFunctionOrInstanceProperty(usage *ast.Node, declaration *ast.Node, declContainer *ast.Node) bool {
for current := usage; current != nil && current != declContainer; current = current.Parent {
return ast.FindAncestorOrQuit(usage, func(current *ast.Node) ast.FindAncestorResult {
if current == declContainer {
return ast.FindAncestorQuit
}
if ast.IsFunctionLike(current) {
return ast.GetImmediatelyInvokedFunctionExpression(current) == nil
return ast.ToFindAncestorResult(ast.GetImmediatelyInvokedFunctionExpression(current) == nil)
}
if ast.IsClassStaticBlockDeclaration(current) {
return declaration.Pos() < usage.Pos()
return ast.ToFindAncestorResult(declaration.Pos() < usage.Pos())
}
if current.Parent != nil && ast.IsPropertyDeclaration(current.Parent) && current.Parent.Initializer() == current {
if ast.IsStatic(current.Parent) {
if ast.IsMethodDeclaration(declaration) {
return true
}
if ast.IsPropertyDeclaration(declaration) && ast.GetContainingClass(usage) == ast.GetContainingClass(declaration) {
propName := declaration.Name()
if ast.IsIdentifier(propName) || ast.IsPrivateIdentifier(propName) {
t := c.getTypeOfSymbol(c.getSymbolOfDeclaration(declaration))
staticBlocks := core.Filter(declaration.Parent.Members(), ast.IsClassStaticBlockDeclaration)
if c.isPropertyInitializedInStaticBlocks(propName, t, staticBlocks, declaration.Parent.Pos(), current.Pos()) {
return true

if current.Parent != nil && ast.IsPropertyDeclaration(current.Parent) {
propertyDeclaration := current.Parent
initializerOfProperty := propertyDeclaration.Initializer() == current
if initializerOfProperty {
if ast.IsStatic(current.Parent) {
if ast.IsMethodDeclaration(declaration) {
return ast.FindAncestorTrue
}
if ast.IsPropertyDeclaration(declaration) && ast.GetContainingClass(usage) == ast.GetContainingClass(declaration) {
propName := declaration.Name()
if ast.IsIdentifier(propName) || ast.IsPrivateIdentifier(propName) {
t := c.getTypeOfSymbol(c.getSymbolOfDeclaration(declaration))
staticBlocks := core.Filter(declaration.Parent.Members(), ast.IsClassStaticBlockDeclaration)
if c.isPropertyInitializedInStaticBlocks(propName, t, staticBlocks, declaration.Parent.Pos(), current.Pos()) {
return ast.FindAncestorTrue
}
}
}
}
} else {
isDeclarationInstanceProperty := ast.IsPropertyDeclaration(declaration) && !ast.IsStatic(declaration)
if !isDeclarationInstanceProperty || ast.GetContainingClass(usage) != ast.GetContainingClass(declaration) {
return true
} else {
isDeclarationInstanceProperty := ast.IsPropertyDeclaration(declaration) && !ast.IsStatic(declaration)
if !isDeclarationInstanceProperty || ast.GetContainingClass(usage) != ast.GetContainingClass(declaration) {
return ast.FindAncestorTrue
}
}
}
}
if current.Parent != nil && ast.IsDecorator(current.Parent) && current.Parent.AsDecorator().Expression == current {

if current.Parent != nil && ast.IsDecorator(current.Parent) {
decorator := current.Parent.AsDecorator()
if ast.IsParameter(decorator.Parent) {
return c.isUsedInFunctionOrInstanceProperty(decorator.Parent.Parent.Parent, declaration, declContainer)
}
if ast.IsMethodDeclaration(decorator.Parent) {
return c.isUsedInFunctionOrInstanceProperty(decorator.Parent.Parent, declaration, declContainer)
if decorator.Expression == current {
if ast.IsParameter(decorator.Parent) {
if c.isUsedInFunctionOrInstanceProperty(decorator.Parent.Parent.Parent, declaration, declContainer) {
return ast.FindAncestorTrue
}
return ast.FindAncestorQuit
}
if ast.IsMethodDeclaration(decorator.Parent) {
if c.isUsedInFunctionOrInstanceProperty(decorator.Parent.Parent, declaration, declContainer) {
return ast.FindAncestorTrue
}
return ast.FindAncestorQuit
}
}
}
}
return false

return ast.FindAncestorFalse
}) != nil
}

func isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration *ast.Node, usage *ast.Node, declContainer *ast.Node) bool {
Expand Down Expand Up @@ -4234,7 +4252,7 @@ func (c *Checker) checkBaseTypeAccessibility(t *Type, node *ast.Node) {
signatures := c.getSignaturesOfType(t, SignatureKindConstruct)
if len(signatures) != 0 {
declaration := signatures[0].declaration
if declaration != nil && HasModifier(declaration, ast.ModifierFlagsPrivate) {
if declaration != nil && ast.HasModifier(declaration, ast.ModifierFlagsPrivate) {
typeClassDeclaration := ast.GetClassLikeDeclarationOfSymbol(t.symbol)
if !c.isNodeWithinClass(node, typeClassDeclaration) {
c.error(node, diagnostics.Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, c.getFullyQualifiedName(t.symbol, nil))
Expand Down Expand Up @@ -6489,7 +6507,7 @@ func (c *Checker) checkAliasSymbol(node *ast.Node) {
name := node.PropertyNameOrName().Text()
c.addTypeOnlyDeclarationRelatedInfo(c.error(node, message, name), core.IfElse(isType, nil, typeOnlyAlias), name)
}
if isType && node.Kind == ast.KindImportEqualsDeclaration && HasModifier(node, ast.ModifierFlagsExport) {
if isType && node.Kind == ast.KindImportEqualsDeclaration && ast.HasModifier(node, ast.ModifierFlagsExport) {
c.error(node, diagnostics.Cannot_use_export_import_on_a_type_or_type_only_namespace_when_0_is_enabled, c.getIsolatedModulesLikeFlagName())
}
case ast.KindExportSpecifier:
Expand Down Expand Up @@ -6777,7 +6795,7 @@ func (c *Checker) checkUnusedClassMembers(node *ast.Node) {
break // Already would have reported an error on the getter.
}
symbol := c.getSymbolOfDeclaration(member)
if !c.isReferenced(symbol) && (HasModifier(member, ast.ModifierFlagsPrivate) || member.Name() != nil && ast.IsPrivateIdentifier(member.Name())) && member.Flags&ast.NodeFlagsAmbient == 0 {
if !c.isReferenced(symbol) && (ast.HasModifier(member, ast.ModifierFlagsPrivate) || member.Name() != nil && ast.IsPrivateIdentifier(member.Name())) && member.Flags&ast.NodeFlagsAmbient == 0 {
c.reportUnused(member, UnusedKindLocal, NewDiagnosticForNode(member.Name(), diagnostics.X_0_is_declared_but_its_value_is_never_read, c.symbolToString(symbol)))
}
case ast.KindConstructor:
Expand Down Expand Up @@ -7029,7 +7047,9 @@ func (c *Checker) getQuickTypeOfExpression(node *ast.Node) *Type {
if isCallChain(expr) {
return c.getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr)
}
return c.getReturnTypeOfSingleNonGenericCallSignature(c.checkNonNullExpression(expr.Expression()))
return c.getReturnTypeOfSingleNonGenericSignature(c.checkNonNullExpression(expr.Expression()), SignatureKindCall)
case ast.IsNewExpression(expr):
return c.getReturnTypeOfSingleNonGenericSignature(c.checkNonNullExpression(expr.Expression()), SignatureKindConstruct)
case ast.IsAssertionExpression(expr) && !ast.IsConstTypeReference(expr.Type()):
return c.getTypeFromTypeNode(expr.Type())
case ast.IsLiteralExpression(node) || ast.IsBooleanLiteral(node):
Expand All @@ -7038,8 +7058,8 @@ func (c *Checker) getQuickTypeOfExpression(node *ast.Node) *Type {
return nil
}

func (c *Checker) getReturnTypeOfSingleNonGenericCallSignature(funcType *Type) *Type {
signature := c.getSingleCallSignature(funcType)
func (c *Checker) getReturnTypeOfSingleNonGenericSignature(funcType *Type, kind SignatureKind) *Type {
signature := c.getSingleSignature(funcType, kind, true /*allowMembers*/)
if signature != nil && len(signature.typeParameters) == 0 {
return c.getReturnTypeOfSignature(signature)
}
Expand All @@ -7049,7 +7069,7 @@ func (c *Checker) getReturnTypeOfSingleNonGenericCallSignature(funcType *Type) *
func (c *Checker) getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr *ast.Node) *Type {
funcType := c.checkExpression(expr.Expression())
nonOptionalType := c.getOptionalExpressionType(funcType, expr.Expression())
returnType := c.getReturnTypeOfSingleNonGenericCallSignature(funcType)
returnType := c.getReturnTypeOfSingleNonGenericSignature(funcType, SignatureKindCall)
if returnType != nil {
return c.propagateOptionalTypeMarker(returnType, expr, nonOptionalType != funcType)
}
Expand Down Expand Up @@ -8335,7 +8355,7 @@ func (c *Checker) resolveNewExpression(node *ast.Node, candidatesOutArray *[]*Si
}
if expressionType.symbol != nil {
valueDecl := ast.GetClassLikeDeclarationOfSymbol(expressionType.symbol)
if valueDecl != nil && HasModifier(valueDecl, ast.ModifierFlagsAbstract) {
if valueDecl != nil && ast.HasModifier(valueDecl, ast.ModifierFlagsAbstract) {
c.error(node, diagnostics.Cannot_create_an_instance_of_an_abstract_class)
return c.resolveErrorCall(node)
}
Expand Down Expand Up @@ -18910,7 +18930,7 @@ func (c *Checker) getIndexInfosOfIndexSymbol(indexSymbol *ast.Symbol, siblingSym
}
forEachType(c.getTypeFromTypeNode(typeNode), func(keyType *Type) {
if c.isValidIndexKeyType(keyType) && findIndexInfo(indexInfos, keyType) == nil {
indexInfo := c.newIndexInfo(keyType, valueType, HasModifier(declaration, ast.ModifierFlagsReadonly), declaration, nil)
indexInfo := c.newIndexInfo(keyType, valueType, ast.HasModifier(declaration, ast.ModifierFlagsReadonly), declaration, nil)
indexInfos = append(indexInfos, indexInfo)
}
})
Expand Down Expand Up @@ -26821,7 +26841,7 @@ func (c *Checker) markPropertyAsReferenced(prop *ast.Symbol, nodeForCheckWriteOn
if prop.Flags&ast.SymbolFlagsClassMember == 0 || prop.ValueDeclaration == nil {
return
}
hasPrivateModifier := HasModifier(prop.ValueDeclaration, ast.ModifierFlagsPrivate)
hasPrivateModifier := ast.HasModifier(prop.ValueDeclaration, ast.ModifierFlagsPrivate)
hasPrivateIdentifier := prop.ValueDeclaration.Name() != nil && ast.IsPrivateIdentifier(prop.ValueDeclaration.Name())
if !hasPrivateModifier && !hasPrivateIdentifier {
return
Expand Down
12 changes: 12 additions & 0 deletions internal/checker/emitresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ func (r *emitResolver) getReferenceResolver() binder.ReferenceResolver {
GetSymbolOfDeclaration: r.checker.getSymbolOfDeclaration,
GetTypeOnlyAliasDeclaration: r.checker.getTypeOnlyAliasDeclarationEx,
GetExportSymbolOfValueSymbolIfExported: r.checker.getExportSymbolOfValueSymbolIfExported,
GetElementAccessExpressionName: r.checker.tryGetElementAccessExpressionName,
})
}
return r.referenceResolver
Expand Down Expand Up @@ -879,6 +880,17 @@ func (r *emitResolver) GetReferencedValueDeclarations(node *ast.IdentifierNode)
return r.getReferenceResolver().GetReferencedValueDeclarations(node)
}

func (r *emitResolver) GetElementAccessExpressionName(expression *ast.ElementAccessExpression) string {
if !ast.IsParseTreeNode(expression.AsNode()) {
return ""
}

r.checkerMu.Lock()
defer r.checkerMu.Unlock()

return r.getReferenceResolver().GetElementAccessExpressionName(expression)
}

// TODO: the emit resolver being responsible for some amount of node construction is a very leaky abstraction,
// and requires giving it access to a lot of context it's otherwise not required to have, which also further complicates the API
// and likely reduces performance. There's probably some refactoring that could be done here to simplify this.
Expand Down
8 changes: 2 additions & 6 deletions internal/checker/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,8 @@ func getSelectedModifierFlags(node *ast.Node, flags ast.ModifierFlags) ast.Modif
return node.ModifierFlags() & flags
}

func HasModifier(node *ast.Node, flags ast.ModifierFlags) bool {
return node.ModifierFlags()&flags != 0
}

func hasReadonlyModifier(node *ast.Node) bool {
return HasModifier(node, ast.ModifierFlagsReadonly)
return ast.HasModifier(node, ast.ModifierFlagsReadonly)
}

func isStaticPrivateIdentifierProperty(s *ast.Symbol) bool {
Expand Down Expand Up @@ -405,7 +401,7 @@ func declarationBelongsToPrivateAmbientMember(declaration *ast.Node) bool {
}

func isPrivateWithinAmbient(node *ast.Node) bool {
return (HasModifier(node, ast.ModifierFlagsPrivate) || ast.IsPrivateIdentifierClassElementDeclaration(node)) && node.Flags&ast.NodeFlagsAmbient != 0
return (ast.HasModifier(node, ast.ModifierFlagsPrivate) || ast.IsPrivateIdentifierClassElementDeclaration(node)) && node.Flags&ast.NodeFlagsAmbient != 0
}

func isTypeAssertion(node *ast.Node) bool {
Expand Down
2 changes: 1 addition & 1 deletion internal/compiler/fileloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func processAllProgramFiles(
includeProcessor: &includeProcessor{},
}
loader.addProjectReferenceTasks(singleThreaded)
loader.resolver = module.NewResolver(loader.projectReferenceFileMapper.host, compilerOptions, opts.TypingsLocation, opts.ProjectName)
loader.resolver = module.NewResolver(loader.projectReferenceFileMapper.host, compilerOptions, opts.TypingsLocation, opts.ProjectName, opts.Host.GetPNPResolutionConfig())
for index, rootFile := range rootFiles {
loader.addRootTask(rootFile, nil, &fileIncludeReason{kind: fileIncludeKindRootFile, data: index})
}
Expand Down
Loading