Skip to content

Commit 4fd6eb1

Browse files
authored
Improve go-to-definition and implement go-to-type-definition (#1405)
1 parent d0520d8 commit 4fd6eb1

File tree

11 files changed

+368
-117
lines changed

11 files changed

+368
-117
lines changed

internal/ast/ast.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2785,6 +2785,10 @@ func (node *SwitchStatement) computeSubtreeFacts() SubtreeFacts {
27852785
propagateSubtreeFacts(node.CaseBlock)
27862786
}
27872787

2788+
func IsSwitchStatement(node *Node) bool {
2789+
return node.Kind == KindSwitchStatement
2790+
}
2791+
27882792
// CaseBlock
27892793

27902794
type CaseBlock struct {

internal/binder/binder.go

Lines changed: 1 addition & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -2758,7 +2758,7 @@ func (b *Binder) errorOrSuggestionOnRange(isError bool, startNode *ast.Node, end
27582758
// If so, the node _must_ be in the current file (as that's the only way anything could have traversed to it to yield it as the error node)
27592759
// This version of `createDiagnosticForNode` uses the binder's context to account for this, and always yields correct diagnostics even in these situations.
27602760
func (b *Binder) createDiagnosticForNode(node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic {
2761-
return ast.NewDiagnostic(b.file, GetErrorRangeForNode(b.file, node), message, args...)
2761+
return ast.NewDiagnostic(b.file, scanner.GetErrorRangeForNode(b.file, node), message, args...)
27622762
}
27632763

27642764
func (b *Binder) addDiagnostic(diagnostic *ast.Diagnostic) {
@@ -2855,83 +2855,3 @@ func isAssignmentDeclaration(decl *ast.Node) bool {
28552855
func isEffectiveModuleDeclaration(node *ast.Node) bool {
28562856
return ast.IsModuleDeclaration(node) || ast.IsIdentifier(node)
28572857
}
2858-
2859-
func getErrorRangeForArrowFunction(sourceFile *ast.SourceFile, node *ast.Node) core.TextRange {
2860-
pos := scanner.SkipTrivia(sourceFile.Text(), node.Pos())
2861-
body := node.AsArrowFunction().Body
2862-
if body != nil && body.Kind == ast.KindBlock {
2863-
startLine, _ := scanner.GetLineAndCharacterOfPosition(sourceFile, body.Pos())
2864-
endLine, _ := scanner.GetLineAndCharacterOfPosition(sourceFile, body.End())
2865-
if startLine < endLine {
2866-
// The arrow function spans multiple lines,
2867-
// make the error span be the first line, inclusive.
2868-
return core.NewTextRange(pos, scanner.GetEndLinePosition(sourceFile, startLine))
2869-
}
2870-
}
2871-
return core.NewTextRange(pos, node.End())
2872-
}
2873-
2874-
func GetErrorRangeForNode(sourceFile *ast.SourceFile, node *ast.Node) core.TextRange {
2875-
errorNode := node
2876-
switch node.Kind {
2877-
case ast.KindSourceFile:
2878-
pos := scanner.SkipTrivia(sourceFile.Text(), 0)
2879-
if pos == len(sourceFile.Text()) {
2880-
return core.NewTextRange(0, 0)
2881-
}
2882-
return scanner.GetRangeOfTokenAtPosition(sourceFile, pos)
2883-
// This list is a work in progress. Add missing node kinds to improve their error spans
2884-
case ast.KindFunctionDeclaration, ast.KindMethodDeclaration:
2885-
if node.Flags&ast.NodeFlagsReparsed != 0 {
2886-
errorNode = node
2887-
break
2888-
}
2889-
fallthrough
2890-
case ast.KindVariableDeclaration, ast.KindBindingElement, ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration,
2891-
ast.KindModuleDeclaration, ast.KindEnumDeclaration, ast.KindEnumMember, ast.KindFunctionExpression,
2892-
ast.KindGetAccessor, ast.KindSetAccessor, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindPropertyDeclaration,
2893-
ast.KindPropertySignature, ast.KindNamespaceImport:
2894-
errorNode = ast.GetNameOfDeclaration(node)
2895-
case ast.KindArrowFunction:
2896-
return getErrorRangeForArrowFunction(sourceFile, node)
2897-
case ast.KindCaseClause, ast.KindDefaultClause:
2898-
start := scanner.SkipTrivia(sourceFile.Text(), node.Pos())
2899-
end := node.End()
2900-
statements := node.AsCaseOrDefaultClause().Statements.Nodes
2901-
if len(statements) != 0 {
2902-
end = statements[0].Pos()
2903-
}
2904-
return core.NewTextRange(start, end)
2905-
case ast.KindReturnStatement, ast.KindYieldExpression:
2906-
pos := scanner.SkipTrivia(sourceFile.Text(), node.Pos())
2907-
return scanner.GetRangeOfTokenAtPosition(sourceFile, pos)
2908-
case ast.KindSatisfiesExpression:
2909-
pos := scanner.SkipTrivia(sourceFile.Text(), node.AsSatisfiesExpression().Expression.End())
2910-
return scanner.GetRangeOfTokenAtPosition(sourceFile, pos)
2911-
case ast.KindConstructor:
2912-
if node.Flags&ast.NodeFlagsReparsed != 0 {
2913-
errorNode = node
2914-
break
2915-
}
2916-
scanner := scanner.GetScannerForSourceFile(sourceFile, node.Pos())
2917-
start := scanner.TokenStart()
2918-
for scanner.Token() != ast.KindConstructorKeyword && scanner.Token() != ast.KindStringLiteral && scanner.Token() != ast.KindEndOfFile {
2919-
scanner.Scan()
2920-
}
2921-
return core.NewTextRange(start, scanner.TokenEnd())
2922-
// !!!
2923-
// case KindJSDocSatisfiesTag:
2924-
// pos := scanner.SkipTrivia(sourceFile.Text(), node.tagName.pos)
2925-
// return scanner.GetRangeOfTokenAtPosition(sourceFile, pos)
2926-
}
2927-
if errorNode == nil {
2928-
// If we don't have a better node, then just set the error on the first token of
2929-
// construct.
2930-
return scanner.GetRangeOfTokenAtPosition(sourceFile, node.Pos())
2931-
}
2932-
pos := errorNode.Pos()
2933-
if !ast.NodeIsMissing(errorNode) && !ast.IsJsxText(errorNode) {
2934-
pos = scanner.SkipTrivia(sourceFile.Text(), pos)
2935-
}
2936-
return core.NewTextRange(pos, errorNode.End())
2937-
}

internal/checker/checker.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30263,6 +30263,22 @@ func (c *Checker) getSymbolAtLocation(node *ast.Node, ignoreErrors bool) *ast.Sy
3026330263
}
3026430264
}
3026530265

30266+
func (c *Checker) getIndexSignaturesAtLocation(node *ast.Node) []*ast.Node {
30267+
var signatures []*ast.Node
30268+
if ast.IsIdentifier(node) && ast.IsPropertyAccessExpression(node.Parent) && node.Parent.Name() == node {
30269+
keyType := c.getLiteralTypeFromPropertyName(node)
30270+
objectType := c.getTypeOfExpression(node.Parent.Expression())
30271+
for _, t := range objectType.Distributed() {
30272+
for _, info := range c.getApplicableIndexInfos(t, keyType) {
30273+
if info.declaration != nil {
30274+
signatures = core.AppendIfUnique(signatures, info.declaration)
30275+
}
30276+
}
30277+
}
30278+
}
30279+
return signatures
30280+
}
30281+
3026630282
func (c *Checker) getSymbolOfNameOrPropertyAccessExpression(name *ast.Node) *ast.Symbol {
3026730283
if ast.IsDeclarationName(name) {
3026830284
return c.getSymbolOfNode(name.Parent)

internal/checker/exports.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,11 @@ func (c *Checker) GetTypeOfPropertyOfType(t *Type, name string) *Type {
142142
func (c *Checker) GetContextualTypeForArgumentAtIndex(node *ast.Node, argIndex int) *Type {
143143
return c.getContextualTypeForArgumentAtIndex(node, argIndex)
144144
}
145+
146+
func (c *Checker) GetIndexSignaturesAtLocation(node *ast.Node) []*ast.Node {
147+
return c.getIndexSignaturesAtLocation(node)
148+
}
149+
150+
func (c *Checker) GetResolvedSymbol(node *ast.Node) *ast.Symbol {
151+
return c.getResolvedSymbol(node)
152+
}

internal/checker/services.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,3 +618,68 @@ func (c *Checker) GetTypeParameterAtPosition(s *Signature, pos int) *Type {
618618
}
619619
return t
620620
}
621+
622+
func (c *Checker) GetContextualDeclarationsForObjectLiteralElement(objectLiteral *ast.Node, name string) []*ast.Node {
623+
var result []*ast.Node
624+
if t := c.getApparentTypeOfContextualType(objectLiteral, ContextFlagsNone); t != nil {
625+
for _, t := range t.Distributed() {
626+
prop := c.getPropertyOfType(t, name)
627+
if prop != nil {
628+
for _, declaration := range prop.Declarations {
629+
result = core.AppendIfUnique(result, declaration)
630+
}
631+
} else {
632+
for _, info := range c.getApplicableIndexInfos(t, c.getStringLiteralType(name)) {
633+
if info.declaration != nil {
634+
result = core.AppendIfUnique(result, info.declaration)
635+
}
636+
}
637+
}
638+
}
639+
}
640+
return result
641+
}
642+
643+
var knownGenericTypeNames = map[string]struct{}{
644+
"Array": {},
645+
"ArrayLike": {},
646+
"ReadonlyArray": {},
647+
"Promise": {},
648+
"PromiseLike": {},
649+
"Iterable": {},
650+
"IterableIterator": {},
651+
"AsyncIterable": {},
652+
"Set": {},
653+
"WeakSet": {},
654+
"ReadonlySet": {},
655+
"Map": {},
656+
"WeakMap": {},
657+
"ReadonlyMap": {},
658+
"Partial": {},
659+
"Required": {},
660+
"Readonly": {},
661+
"Pick": {},
662+
"Omit": {},
663+
"NonNullable": {},
664+
}
665+
666+
func isKnownGenericTypeName(name string) bool {
667+
_, exists := knownGenericTypeNames[name]
668+
return exists
669+
}
670+
671+
func (c *Checker) GetFirstTypeArgumentFromKnownType(t *Type) *Type {
672+
if t.objectFlags&ObjectFlagsReference != 0 && isKnownGenericTypeName(t.symbol.Name) {
673+
symbol := c.getGlobalSymbol(t.symbol.Name, ast.SymbolFlagsType, nil)
674+
if symbol != nil && symbol == t.Target().symbol {
675+
return core.FirstOrNil(c.getTypeArguments(t))
676+
}
677+
}
678+
if t.alias != nil && isKnownGenericTypeName(t.alias.symbol.Name) {
679+
symbol := c.getGlobalSymbol(t.alias.symbol.Name, ast.SymbolFlagsType, nil)
680+
if symbol != nil && symbol == t.alias.symbol {
681+
return core.FirstOrNil(t.alias.typeArguments)
682+
}
683+
}
684+
return nil
685+
}

internal/checker/utilities.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func NewDiagnosticForNode(node *ast.Node, message *diagnostics.Message, args ...
2323
var loc core.TextRange
2424
if node != nil {
2525
file = ast.GetSourceFileOfNode(node)
26-
loc = binder.GetErrorRangeForNode(file, node)
26+
loc = scanner.GetErrorRangeForNode(file, node)
2727
}
2828
return ast.NewDiagnostic(file, loc, message, args...)
2929
}

0 commit comments

Comments
 (0)