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
18 changes: 18 additions & 0 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,16 @@ func (n *Node) ModuleSpecifier() *Expression {
panic("Unhandled case in Node.ModuleSpecifier: " + n.Kind.String())
}

func (n *Node) ImportClause() *Node {
switch n.Kind {
case KindImportDeclaration, KindJSImportDeclaration:
return n.AsImportDeclaration().ImportClause
case KindJSDocImportTag:
return n.AsJSDocImportTag().ImportClause
}
panic("Unhandled case in Node.ImportClause: " + n.Kind.String())
}

func (n *Node) Statement() *Statement {
switch n.Kind {
case KindDoStatement:
Expand Down Expand Up @@ -10117,6 +10127,10 @@ func (node *JSDocCallbackTag) Clone(f NodeFactoryCoercible) *Node {
return cloneNode(f.AsNodeFactory().NewJSDocCallbackTag(node.TagName, node.TypeExpression, node.FullName, node.Comment), node.AsNode(), f.AsNodeFactory().hooks)
}

func IsJSDocCallbackTag(node *Node) bool {
return node.Kind == KindJSDocCallbackTag
}

// JSDocOverloadTag
type JSDocOverloadTag struct {
JSDocTagBase
Expand Down Expand Up @@ -10190,6 +10204,10 @@ func (node *JSDocTypedefTag) Clone(f NodeFactoryCoercible) *Node {

func (node *JSDocTypedefTag) Name() *DeclarationName { return node.name }

func IsJSDocTypedefTag(node *Node) bool {
return node.Kind == KindJSDocTypedefTag
}

// JSDocTypeLiteral
type JSDocTypeLiteral struct {
TypeNodeBase
Expand Down
4 changes: 4 additions & 0 deletions internal/checker/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,7 @@ func (c *Checker) GetIndexSignaturesAtLocation(node *ast.Node) []*ast.Node {
func (c *Checker) GetResolvedSymbol(node *ast.Node) *ast.Symbol {
return c.getResolvedSymbol(node)
}

func (c *Checker) GetImmediateAliasedSymbol(symbol *ast.Symbol) *ast.Symbol {
return c.getImmediateAliasedSymbol(symbol)
}
150 changes: 132 additions & 18 deletions internal/checker/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,14 +347,14 @@ func runWithoutResolvedSignatureCaching[T any](c *Checker, node *ast.Node, fn fu

func (c *Checker) GetRootSymbols(symbol *ast.Symbol) []*ast.Symbol {
roots := c.getImmediateRootSymbols(symbol)
if roots != nil {
var result []*ast.Symbol
for _, root := range roots {
result = append(result, c.GetRootSymbols(root)...)
}
return result
if len(roots) == 0 {
return []*ast.Symbol{symbol}
}
return []*ast.Symbol{symbol}
var result []*ast.Symbol
for _, root := range roots {
result = append(result, c.GetRootSymbols(root)...)
}
return result
}

func (c *Checker) getImmediateRootSymbols(symbol *ast.Symbol) []*ast.Symbol {
Expand All @@ -364,7 +364,8 @@ func (c *Checker) getImmediateRootSymbols(symbol *ast.Symbol) []*ast.Symbol {
func(t *Type) *ast.Symbol {
return c.getPropertyOfType(t, symbol.Name)
})
} else if symbol.Flags&ast.SymbolFlagsTransient != 0 {
}
if symbol.Flags&ast.SymbolFlagsTransient != 0 {
if c.spreadLinks.Has(symbol) {
leftSpread := c.spreadLinks.Get(symbol).leftSpread
rightSpread := c.spreadLinks.Get(symbol).rightSpread
Expand All @@ -382,9 +383,7 @@ func (c *Checker) getImmediateRootSymbols(symbol *ast.Symbol) []*ast.Symbol {
if target != nil {
return []*ast.Symbol{target}
}
return nil
}

return nil
}

Expand Down Expand Up @@ -418,20 +417,16 @@ func (c *Checker) GetExportSpecifierLocalTargetSymbol(node *ast.Node) *ast.Symbo
if node.Parent.Parent.AsExportDeclaration().ModuleSpecifier != nil {
return c.getExternalModuleMember(node.Parent.Parent, node, false /*dontResolveAlias*/)
}
name := node.PropertyName()
if name == nil {
name = node.Name()
}
name := node.PropertyNameOrName()
if name.Kind == ast.KindStringLiteral {
// Skip for invalid syntax like this: export { "x" }
return nil
}
return c.resolveEntityName(name, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, true /*ignoreErrors*/, false, nil)
case ast.KindIdentifier:
// do nothing (don't panic)
default:
panic("Unhandled case in getExportSpecifierLocalTargetSymbol, node should be ExportSpecifier | Identifier")
return c.resolveEntityName(node, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, true /*ignoreErrors*/, false, nil)
}
return c.resolveEntityName(node, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, true /*ignoreErrors*/, false, nil)
panic("Unhandled case in getExportSpecifierLocalTargetSymbol, node should be ExportSpecifier | Identifier")
}

func (c *Checker) GetShorthandAssignmentValueSymbol(location *ast.Node) *ast.Symbol {
Expand Down Expand Up @@ -683,3 +678,122 @@ func (c *Checker) GetFirstTypeArgumentFromKnownType(t *Type) *Type {
}
return nil
}

// Gets all symbols for one property. Does not get symbols for every property.
func (c *Checker) GetPropertySymbolsFromContextualType(node *ast.Node, contextualType *Type, unionSymbolOk bool) []*ast.Symbol {
name := ast.GetTextOfPropertyName(node.Name())
if name == "" {
return nil
}
if contextualType.flags&TypeFlagsUnion == 0 {
if symbol := c.getPropertyOfType(contextualType, name); symbol != nil {
return []*ast.Symbol{symbol}
}
return nil
}
filteredTypes := contextualType.Types()
if ast.IsObjectLiteralExpression(node.Parent) || ast.IsJsxAttributes(node.Parent) {
filteredTypes = core.Filter(filteredTypes, func(t *Type) bool {
return !c.IsTypeInvalidDueToUnionDiscriminant(t, node.Parent)
})
}
discriminatedPropertySymbols := core.MapNonNil(filteredTypes, func(t *Type) *ast.Symbol {
return c.getPropertyOfType(t, name)
})
if unionSymbolOk && (len(discriminatedPropertySymbols) == 0 || len(discriminatedPropertySymbols) == len(contextualType.Types())) {
if symbol := c.getPropertyOfType(contextualType, name); symbol != nil {
return []*ast.Symbol{symbol}
}
}
if len(filteredTypes) == 0 && len(discriminatedPropertySymbols) == 0 {
// Bad discriminant -- do again without discriminating
return core.MapNonNil(contextualType.Types(), func(t *Type) *ast.Symbol {
return c.getPropertyOfType(t, name)
})
}
// by eliminating duplicates we might even end up with a single symbol
// that helps with displaying better quick infos on properties of union types
return core.Deduplicate(discriminatedPropertySymbols)
}

// Gets the property symbol corresponding to the property in destructuring assignment
// 'property1' from
//
// for ( { property1: a } of elems) {
// }
//
// 'property1' at location 'a' from:
//
// [a] = [ property1, property2 ]
func (c *Checker) GetPropertySymbolOfDestructuringAssignment(location *ast.Node) *ast.Symbol {
if isArrayLiteralOrObjectLiteralDestructuringPattern(location.Parent.Parent) {
// Get the type of the object or array literal and then look for property of given name in the type
if typeOfObjectLiteral := c.getTypeOfAssignmentPattern(location.Parent.Parent); typeOfObjectLiteral != nil {
return c.getPropertyOfType(typeOfObjectLiteral, location.Text())
}
}
return nil
}

// Gets the type of object literal or array literal of destructuring assignment.
// { a } from
//
// for ( { a } of elems) {
// }
//
// [ a ] from
//
// [a] = [ some array ...]
func (c *Checker) getTypeOfAssignmentPattern(expr *ast.Node) *Type {
// If this is from "for of"
// for ( { a } of elems) {
// }
if ast.IsForOfStatement(expr.Parent) {
iteratedType := c.checkRightHandSideOfForOf(expr.Parent)
return c.checkDestructuringAssignment(expr, core.OrElse(iteratedType, c.errorType), CheckModeNormal, false)
}
// If this is from "for" initializer
// for ({a } = elems[0];.....) { }
if ast.IsBinaryExpression(expr.Parent) {
iteratedType := c.getTypeOfExpression(expr.Parent.AsBinaryExpression().Right)
return c.checkDestructuringAssignment(expr, core.OrElse(iteratedType, c.errorType), CheckModeNormal, false)
}
// If this is from nested object binding pattern
// for ({ skills: { primary, secondary } } = multiRobot, i = 0; i < 1; i++) {
if ast.IsPropertyAssignment(expr.Parent) {
node := expr.Parent.Parent
typeOfParentObjectLiteral := core.OrElse(c.getTypeOfAssignmentPattern(node), c.errorType)
propertyIndex := slices.Index(node.AsObjectLiteralExpression().Properties.Nodes, expr.Parent)
return c.checkObjectLiteralDestructuringPropertyAssignment(node, typeOfParentObjectLiteral, propertyIndex, nil, false)
}
// Array literal assignment - array destructuring pattern
node := expr.Parent
// [{ property1: p1, property2 }] = elems;
typeOfArrayLiteral := core.OrElse(c.getTypeOfAssignmentPattern(node), c.errorType)
elementType := core.OrElse(c.checkIteratedTypeOrElementType(IterationUseDestructuring, typeOfArrayLiteral, c.undefinedType, expr.Parent), c.errorType)
return c.checkArrayLiteralDestructuringElementAssignment(node, typeOfArrayLiteral, slices.Index(node.AsArrayLiteralExpression().Elements.Nodes, expr), elementType, CheckModeNormal)
}

func isArrayLiteralOrObjectLiteralDestructuringPattern(node *ast.Node) bool {
if !(ast.IsArrayLiteralExpression(node) || ast.IsObjectLiteralExpression(node)) {
return false
}
parent := node.Parent
// [a,b,c] from:
// [a, b, c] = someExpression;
if ast.IsBinaryExpression(parent) && parent.AsBinaryExpression().Left == node && parent.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken {
return true
}
// [a, b, c] from:
// for([a, b, c] of expression)
if ast.IsForOfStatement(parent) && parent.Initializer() == node {
return true
}
// {x, a: {a, b, c} } = someExpression
if ast.IsPropertyAssignment(parent) {
return isArrayLiteralOrObjectLiteralDestructuringPattern(parent.Parent)
}
// [a, b, c] of
// [x, [a, b, c] ] = someExpression
return isArrayLiteralOrObjectLiteralDestructuringPattern(parent)
}
24 changes: 18 additions & 6 deletions internal/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,12 +351,6 @@ func Coalesce[T *U, U any](a T, b T) T {
}
}

// Returns the first element that is not `nil`; CoalesceList(a, b, c) is roughly analogous to `a ?? b ?? c` in JS, except that it
// non-shortcutting, so it is advised to only use a constant or precomputed value for non-first values in the list
func CoalesceList[T *U, U any](a ...T) T {
return FirstNonNil(a, func(t T) T { return t })
}

func ComputeLineStarts(text string) []TextPos {
result := make([]TextPos, 0, strings.Count(text, "\n")+1)
return slices.AppendSeq(result, ComputeLineStartsSeq(text))
Expand Down Expand Up @@ -625,3 +619,21 @@ func CopyMapInto[M1 ~map[K]V, M2 ~map[K]V, K comparable, V any](dst M1, src M2)
maps.Copy(dst, src)
return dst
}

func Deduplicate[T comparable](slice []T) []T {
if len(slice) > 1 {
for i, value := range slice {
if slices.Contains(slice[:i], value) {
result := slices.Clone(slice[:i])
for i++; i < len(slice); i++ {
value = slice[i]
if !slices.Contains(result, value) {
result = append(result, value)
}
}
return result
}
}
}
return slice
}
Loading
Loading