Skip to content

Commit cdff2b1

Browse files
authored
Signature help implementation (#861)
1 parent ad7418e commit cdff2b1

File tree

12 files changed

+2588
-90
lines changed

12 files changed

+2588
-90
lines changed

internal/ast/utilities.go

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1948,7 +1948,10 @@ func IsComputedNonLiteralName(name *Node) bool {
19481948
}
19491949

19501950
func IsQuestionToken(node *Node) bool {
1951-
return node != nil && node.Kind == KindQuestionToken
1951+
if node == nil {
1952+
return false
1953+
}
1954+
return node.Kind == KindQuestionToken
19521955
}
19531956

19541957
func GetTextOfPropertyName(name *Node) string {
@@ -3385,3 +3388,75 @@ func IsRightSideOfQualifiedNameOrPropertyAccess(node *Node) bool {
33853388
}
33863389
return false
33873390
}
3391+
3392+
func HasQuestionToken(node *Node) bool {
3393+
switch node.Kind {
3394+
case KindParameter:
3395+
return node.AsParameterDeclaration().QuestionToken != nil
3396+
case KindMethodDeclaration:
3397+
return IsQuestionToken(node.AsMethodDeclaration().PostfixToken)
3398+
case KindShorthandPropertyAssignment:
3399+
return IsQuestionToken(node.AsShorthandPropertyAssignment().PostfixToken)
3400+
case KindMethodSignature:
3401+
return IsQuestionToken(node.AsMethodSignatureDeclaration().PostfixToken)
3402+
case KindPropertySignature:
3403+
return IsQuestionToken(node.AsPropertySignatureDeclaration().PostfixToken)
3404+
case KindPropertyAssignment:
3405+
return IsQuestionToken(node.AsPropertyAssignment().PostfixToken)
3406+
case KindPropertyDeclaration:
3407+
return IsQuestionToken(node.AsPropertyDeclaration().PostfixToken)
3408+
}
3409+
return false
3410+
}
3411+
3412+
func IsJsxOpeningLikeElement(node *Node) bool {
3413+
return IsJsxOpeningElement(node) || IsJsxSelfClosingElement(node)
3414+
}
3415+
3416+
func GetInvokedExpression(node *Node) *Node {
3417+
switch node.Kind {
3418+
case KindTaggedTemplateExpression:
3419+
return node.AsTaggedTemplateExpression().Tag
3420+
case KindJsxOpeningElement, KindJsxSelfClosingElement:
3421+
return node.TagName()
3422+
case KindBinaryExpression:
3423+
return node.AsBinaryExpression().Right
3424+
default:
3425+
return node.Expression()
3426+
}
3427+
}
3428+
3429+
func IsCallOrNewExpression(node *Node) bool {
3430+
return IsCallExpression(node) || IsNewExpression(node)
3431+
}
3432+
3433+
func CanHaveSymbol(node *Node) bool {
3434+
switch node.Kind {
3435+
case KindArrowFunction, KindBinaryExpression, KindBindingElement, KindCallExpression, KindCallSignature,
3436+
KindClassDeclaration, KindClassExpression, KindClassStaticBlockDeclaration, KindConstructor, KindConstructorType,
3437+
KindConstructSignature, KindElementAccessExpression, KindEnumDeclaration, KindEnumMember, KindExportAssignment, KindJSExportAssignment,
3438+
KindExportDeclaration, KindExportSpecifier, KindFunctionDeclaration, KindFunctionExpression, KindFunctionType,
3439+
KindGetAccessor, KindIdentifier, KindImportClause, KindImportEqualsDeclaration, KindImportSpecifier,
3440+
KindIndexSignature, KindInterfaceDeclaration, KindJSDocSignature, KindJSDocTypeLiteral,
3441+
KindJsxAttribute, KindJsxAttributes, KindJsxSpreadAttribute, KindMappedType, KindMethodDeclaration,
3442+
KindMethodSignature, KindModuleDeclaration, KindNamedTupleMember, KindNamespaceExport, KindNamespaceExportDeclaration,
3443+
KindNamespaceImport, KindNewExpression, KindNoSubstitutionTemplateLiteral, KindNumericLiteral, KindObjectLiteralExpression,
3444+
KindParameter, KindPropertyAccessExpression, KindPropertyAssignment, KindPropertyDeclaration, KindPropertySignature,
3445+
KindSetAccessor, KindShorthandPropertyAssignment, KindSourceFile, KindSpreadAssignment, KindStringLiteral,
3446+
KindTypeAliasDeclaration, KindJSTypeAliasDeclaration, KindTypeLiteral, KindTypeParameter, KindVariableDeclaration:
3447+
return true
3448+
}
3449+
return false
3450+
}
3451+
3452+
func IndexOfNode(nodes []*Node, node *Node) int {
3453+
index, ok := slices.BinarySearchFunc(nodes, node, compareNodePositions)
3454+
if ok {
3455+
return index
3456+
}
3457+
return -1
3458+
}
3459+
3460+
func compareNodePositions(n1, n2 *Node) int {
3461+
return n1.Pos() - n2.Pos()
3462+
}

internal/checker/checker.go

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7713,7 +7713,7 @@ func (c *Checker) createArrayLiteralType(t *Type) *Type {
77137713

77147714
func isSpreadIntoCallOrNew(node *ast.Node) bool {
77157715
parent := ast.WalkUpParenthesizedExpressions(node.Parent)
7716-
return ast.IsSpreadElement(parent) && isCallOrNewExpression(parent.Parent)
7716+
return ast.IsSpreadElement(parent) && ast.IsCallOrNewExpression(parent.Parent)
77177717
}
77187718

77197719
func (c *Checker) checkQualifiedName(node *ast.Node, checkMode CheckMode) *Type {
@@ -7953,7 +7953,7 @@ func (c *Checker) checkDeprecatedSignature(sig *Signature, node *ast.Node) {
79537953
}
79547954
if sig.declaration != nil && sig.declaration.Flags&ast.NodeFlagsDeprecated != 0 {
79557955
suggestionNode := c.getDeprecatedSuggestionNode(node)
7956-
name := tryGetPropertyAccessOrIdentifierToString(getInvokedExpression(node))
7956+
name := tryGetPropertyAccessOrIdentifierToString(ast.GetInvokedExpression(node))
79577957
c.addDeprecatedSuggestionWithSignature(suggestionNode, sig.declaration, name, c.signatureToString(sig))
79587958
}
79597959
}
@@ -8419,7 +8419,7 @@ type CallState struct {
84198419
func (c *Checker) resolveCall(node *ast.Node, signatures []*Signature, candidatesOutArray *[]*Signature, checkMode CheckMode, callChainFlags SignatureFlags, headMessage *diagnostics.Message) *Signature {
84208420
isTaggedTemplate := node.Kind == ast.KindTaggedTemplateExpression
84218421
isDecorator := node.Kind == ast.KindDecorator
8422-
isJsxOpeningOrSelfClosingElement := isJsxOpeningLikeElement(node)
8422+
isJsxOpeningOrSelfClosingElement := ast.IsJsxOpeningLikeElement(node)
84238423
isInstanceof := node.Kind == ast.KindBinaryExpression
84248424
reportErrors := !c.isInferencePartiallyBlocked && candidatesOutArray == nil
84258425
var s CallState
@@ -8725,7 +8725,7 @@ func (c *Checker) hasCorrectArity(node *ast.Node, args []*ast.Node, signature *S
87258725
argCount = c.getDecoratorArgumentCount(node, signature)
87268726
case ast.IsBinaryExpression(node):
87278727
argCount = 1
8728-
case isJsxOpeningLikeElement(node):
8728+
case ast.IsJsxOpeningLikeElement(node):
87298729
callIsIncomplete = node.Attributes().End() == node.End()
87308730
if callIsIncomplete {
87318731
return true
@@ -8845,7 +8845,7 @@ func (c *Checker) checkTypeArguments(signature *Signature, typeArgumentNodes []*
88458845
}
88468846

88478847
func (c *Checker) isSignatureApplicable(node *ast.Node, args []*ast.Node, signature *Signature, relation *Relation, checkMode CheckMode, reportErrors bool, inferenceContext *InferenceContext, diagnosticOutput *[]*ast.Diagnostic) bool {
8848-
if isJsxOpeningLikeElement(node) {
8848+
if ast.IsJsxOpeningLikeElement(node) {
88498849
return c.checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation, checkMode, reportErrors, diagnosticOutput)
88508850
}
88518851
thisType := c.getThisTypeOfSignature(signature)
@@ -8986,7 +8986,7 @@ func (c *Checker) getEffectiveCheckNode(argument *ast.Node) *ast.Node {
89868986
}
89878987

89888988
func (c *Checker) inferTypeArguments(node *ast.Node, signature *Signature, args []*ast.Node, checkMode CheckMode, context *InferenceContext) []*Type {
8989-
if isJsxOpeningLikeElement(node) {
8989+
if ast.IsJsxOpeningLikeElement(node) {
89908990
return c.inferJsxTypeArguments(node, signature, checkMode, context)
89918991
}
89928992
// If a contextual type is available, infer from that type to the return type of the call expression. For
@@ -10835,7 +10835,7 @@ func (c *Checker) isMethodAccessForCall(node *ast.Node) bool {
1083510835
for ast.IsParenthesizedExpression(node.Parent) {
1083610836
node = node.Parent
1083710837
}
10838-
return isCallOrNewExpression(node.Parent) && node.Parent.Expression() == node
10838+
return ast.IsCallOrNewExpression(node.Parent) && node.Parent.Expression() == node
1083910839
}
1084010840

1084110841
// Lookup the private identifier lexically.
@@ -11051,7 +11051,7 @@ func (c *Checker) isUncalledFunctionReference(node *ast.Node, symbol *ast.Symbol
1105111051
parent = node.Parent
1105211052
}
1105311053
if ast.IsCallLikeExpression(parent) {
11054-
return isCallOrNewExpression(parent) && ast.IsIdentifier(node) && c.hasMatchingArgument(parent, node)
11054+
return ast.IsCallOrNewExpression(parent) && ast.IsIdentifier(node) && c.hasMatchingArgument(parent, node)
1105511055
}
1105611056
return core.Every(symbol.Declarations, func(d *ast.Node) bool {
1105711057
return !ast.IsFunctionLike(d) || c.IsDeprecatedDeclaration(d)
@@ -14179,7 +14179,7 @@ func (c *Checker) getTargetOfAliasLikeExpression(expression *ast.Node, dontResol
1417914179
}
1418014180

1418114181
func (c *Checker) getTargetOfNamespaceExportDeclaration(node *ast.Node, dontResolveAlias bool) *ast.Symbol {
14182-
if canHaveSymbol(node.Parent) {
14182+
if ast.CanHaveSymbol(node.Parent) {
1418314183
resolved := c.resolveExternalModuleSymbol(node.Parent.Symbol(), dontResolveAlias)
1418414184
c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "")
1418514185
return resolved
@@ -26275,18 +26275,6 @@ func (c *Checker) markPropertyAsReferenced(prop *ast.Symbol, nodeForCheckWriteOn
2627526275
c.symbolReferenceLinks.Get(target).referenceKinds |= ast.SymbolFlagsAll
2627626276
}
2627726277

26278-
func (c *Checker) GetExpandedParameters(signature *Signature /* !!! skipUnionExpanding */) []*ast.Symbol {
26279-
if signatureHasRestParameter(signature) {
26280-
restIndex := len(signature.parameters) - 1
26281-
restSymbol := signature.parameters[restIndex]
26282-
restType := c.getTypeOfSymbol(restSymbol)
26283-
if isTupleType(restType) {
26284-
return c.expandSignatureParametersWithTupleMembers(signature, restType.AsTypeReference(), restIndex, restSymbol)
26285-
}
26286-
}
26287-
return signature.parameters
26288-
}
26289-
2629026278
func (c *Checker) expandSignatureParametersWithTupleMembers(signature *Signature, restType *TypeReference, restIndex int, restSymbol *ast.Symbol) []*ast.Symbol {
2629126279
elementTypes := c.getTypeArguments(restType.AsType())
2629226280
elementInfos := restType.TargetTupleType().elementInfos
@@ -26765,7 +26753,7 @@ func (c *Checker) markLinkedReferences(location *ast.Node, hint ReferenceHint, p
2676526753
c.markExportAssignmentAliasReferenced(location)
2676626754
return
2676726755
}
26768-
if isJsxOpeningLikeElement(location) || ast.IsJsxOpeningFragment(location) {
26756+
if ast.IsJsxOpeningLikeElement(location) || ast.IsJsxOpeningFragment(location) {
2676926757
c.markJsxAliasReferenced(location)
2677026758
return
2677126759
}
@@ -26930,7 +26918,7 @@ func (c *Checker) markJsxAliasReferenced(node *ast.Node /*JsxOpeningLikeElement
2693026918
jsxFactoryRefErr := core.IfElse(c.compilerOptions.Jsx == core.JsxEmitReact, diagnostics.This_JSX_tag_requires_0_to_be_in_scope_but_it_could_not_be_found, nil)
2693126919
jsxFactoryNamespace := c.getJsxNamespace(node)
2693226920
jsxFactoryLocation := node
26933-
if isJsxOpeningLikeElement(node) {
26921+
if ast.IsJsxOpeningLikeElement(node) {
2693426922
jsxFactoryLocation = node.TagName()
2693526923
}
2693626924
// allow null as jsxFragmentFactory
@@ -27572,7 +27560,7 @@ func (c *Checker) getContextualType(node *ast.Node, contextFlags ContextFlags) *
2757227560
return c.getContextualType(parent.Parent, contextFlags)
2757327561
case ast.KindArrayLiteralExpression:
2757427562
t := c.getApparentTypeOfContextualType(parent, contextFlags)
27575-
elementIndex := indexOfNode(parent.AsArrayLiteralExpression().Elements.Nodes, node)
27563+
elementIndex := ast.IndexOfNode(parent.AsArrayLiteralExpression().Elements.Nodes, node)
2757627564
firstSpreadIndex, lastSpreadIndex := c.getSpreadIndices(parent)
2757727565
return c.getContextualTypeForElementExpression(t, elementIndex, len(parent.AsArrayLiteralExpression().Elements.Nodes), firstSpreadIndex, lastSpreadIndex)
2757827566
case ast.KindConditionalExpression:
@@ -27975,7 +27963,7 @@ func (c *Checker) getContextualTypeForArgumentAtIndex(callTarget *ast.Node, argI
2797527963
} else {
2797627964
signature = c.getResolvedSignature(callTarget, nil, CheckModeNormal)
2797727965
}
27978-
if isJsxOpeningLikeElement(callTarget) && argIndex == 0 {
27966+
if ast.IsJsxOpeningLikeElement(callTarget) && argIndex == 0 {
2797927967
return c.getEffectiveFirstArgumentForJsxSignature(signature, callTarget)
2798027968
}
2798127969
restIndex := len(signature.parameters) - 1
@@ -28229,7 +28217,7 @@ func (c *Checker) getEffectiveCallArguments(node *ast.Node) []*ast.Node {
2822928217
case ast.IsBinaryExpression(node):
2823028218
// Handles instanceof operator
2823128219
return []*ast.Node{node.AsBinaryExpression().Left}
28232-
case isJsxOpeningLikeElement(node):
28220+
case ast.IsJsxOpeningLikeElement(node):
2823328221
if len(node.Attributes().AsJsxAttributes().Properties.Nodes) != 0 || (ast.IsJsxOpeningElement(node) && len(node.Parent.Children().Nodes) != 0) {
2823428222
return []*ast.Node{node.Attributes()}
2823528223
}

internal/checker/exports.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,35 @@ func (c *Checker) GetEffectiveDeclarationFlags(n *ast.Node, flagsToCheck ast.Mod
8585
func (c *Checker) GetBaseConstraintOfType(t *Type) *Type {
8686
return c.getBaseConstraintOfType(t)
8787
}
88+
89+
func (c *Checker) GetTypePredicateOfSignature(sig *Signature) *TypePredicate {
90+
return c.getTypePredicateOfSignature(sig)
91+
}
92+
93+
func IsTupleType(t *Type) bool {
94+
return isTupleType(t)
95+
}
96+
97+
func (c *Checker) GetReturnTypeOfSignature(sig *Signature) *Type {
98+
return c.getReturnTypeOfSignature(sig)
99+
}
100+
101+
func (c *Checker) HasEffectiveRestParameter(signature *Signature) bool {
102+
return c.hasEffectiveRestParameter(signature)
103+
}
104+
105+
func (c *Checker) GetLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol *ast.Symbol) []*Type {
106+
return c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
107+
}
108+
109+
func (c *Checker) GetContextualTypeForObjectLiteralElement(element *ast.Node, contextFlags ContextFlags) *Type {
110+
return c.getContextualTypeForObjectLiteralElement(element, contextFlags)
111+
}
112+
113+
func (c *Checker) TypePredicateToString(t *TypePredicate) string {
114+
return c.typePredicateToString(t)
115+
}
116+
117+
func (c *Checker) GetExpandedParameters(signature *Signature, skipUnionExpanding bool) [][]*ast.Symbol {
118+
return c.getExpandedParameters(signature, skipUnionExpanding)
119+
}

internal/checker/jsx.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func (c *Checker) checkJsxAttributes(node *ast.Node, checkMode CheckMode) *Type
119119
}
120120

121121
func (c *Checker) checkJsxOpeningLikeElementOrOpeningFragment(node *ast.Node) {
122-
isNodeOpeningLikeElement := isJsxOpeningLikeElement(node)
122+
isNodeOpeningLikeElement := ast.IsJsxOpeningLikeElement(node)
123123
if isNodeOpeningLikeElement {
124124
c.checkGrammarJsxElement(node)
125125
}

internal/checker/relater.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2693,7 +2693,7 @@ func (r *Relater) hasExcessProperties(source *Type, target *Type, reportErrors b
26932693
if r.errorNode == nil {
26942694
panic("No errorNode in hasExcessProperties")
26952695
}
2696-
if ast.IsJsxAttributes(r.errorNode) || isJsxOpeningLikeElement(r.errorNode) || isJsxOpeningLikeElement(r.errorNode.Parent) {
2696+
if ast.IsJsxAttributes(r.errorNode) || ast.IsJsxOpeningLikeElement(r.errorNode) || ast.IsJsxOpeningLikeElement(r.errorNode.Parent) {
26972697
// JsxAttributes has an object-literal flag and undergo same type-assignablity check as normal object-literal.
26982698
// However, using an object-literal error message will be very confusing to the users so we give different a message.
26992699
if prop.ValueDeclaration != nil && ast.IsJsxAttribute(prop.ValueDeclaration) && ast.GetSourceFileOfNode(r.errorNode) == ast.GetSourceFileOfNode(prop.ValueDeclaration.Name()) {

internal/checker/services.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/microsoft/typescript-go/internal/ast"
88
"github.com/microsoft/typescript-go/internal/core"
9+
"github.com/microsoft/typescript-go/internal/printer"
910
)
1011

1112
func (c *Checker) GetSymbolsInScope(location *ast.Node, meaning ast.SymbolFlags) []*ast.Symbol {
@@ -299,10 +300,20 @@ func runWithInferenceBlockedFromSourceNode[T any](c *Checker, node *ast.Node, fn
299300
return result
300301
}
301302

302-
func runWithoutResolvedSignatureCaching[T any](c *Checker, node *ast.Node, fn func() T) T {
303-
ancestorNode := ast.FindAncestor(node, func(n *ast.Node) bool {
304-
return ast.IsCallLikeOrFunctionLikeExpression(n)
303+
func GetResolvedSignatureForSignatureHelp(node *ast.Node, argumentCount int, c *Checker) (*Signature, []*Signature) {
304+
type result struct {
305+
signature *Signature
306+
candidates []*Signature
307+
}
308+
res := runWithoutResolvedSignatureCaching(c, node, func() result {
309+
signature, candidates := c.getResolvedSignatureWorker(node, CheckModeIsForSignatureHelp, argumentCount)
310+
return result{signature, candidates}
305311
})
312+
return res.signature, res.candidates
313+
}
314+
315+
func runWithoutResolvedSignatureCaching[T any](c *Checker, node *ast.Node, fn func() T) T {
316+
ancestorNode := ast.FindAncestor(node, ast.IsCallLikeOrFunctionLikeExpression)
306317
if ancestorNode != nil {
307318
cachedResolvedSignatures := make(map[*SignatureLinks]*Signature)
308319
cachedTypes := make(map[*ValueSymbolLinks]*Type)
@@ -506,3 +517,15 @@ func (c *Checker) GetConstantValue(node *ast.Node) any {
506517

507518
return nil
508519
}
520+
521+
func (c *Checker) getResolvedSignatureWorker(node *ast.Node, checkMode CheckMode, argumentCount int) (*Signature, []*Signature) {
522+
parsedNode := printer.NewEmitContext().ParseNode(node)
523+
c.apparentArgumentCount = &argumentCount
524+
candidatesOutArray := &[]*Signature{}
525+
var res *Signature
526+
if parsedNode != nil {
527+
res = c.getResolvedSignature(parsedNode, candidatesOutArray, checkMode)
528+
}
529+
c.apparentArgumentCount = nil
530+
return res, *candidatesOutArray
531+
}

internal/checker/types.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,10 @@ func (t *Type) Flags() TypeFlags {
588588
return t.flags
589589
}
590590

591+
func (t *Type) ObjectFlags() ObjectFlags {
592+
return t.objectFlags
593+
}
594+
591595
// Casts for concrete struct types
592596

593597
func (t *Type) AsIntrinsicType() *IntrinsicType { return t.data.(*IntrinsicType) }
@@ -940,6 +944,8 @@ type TupleElementInfo struct {
940944
labeledDeclaration *ast.Node // NamedTupleMember | ParameterDeclaration | nil
941945
}
942946

947+
func (t *TupleElementInfo) TupleElementFlags() ElementFlags { return t.flags }
948+
943949
type TupleType struct {
944950
InterfaceType
945951
elementInfos []TupleElementInfo
@@ -949,6 +955,15 @@ type TupleType struct {
949955
readonly bool
950956
}
951957

958+
func (t *TupleType) FixedLength() int { return t.fixedLength }
959+
func (t *TupleType) ElementFlags() []ElementFlags {
960+
elementFlags := make([]ElementFlags, len(t.elementInfos))
961+
for i, info := range t.elementInfos {
962+
elementFlags[i] = info.flags
963+
}
964+
return elementFlags
965+
}
966+
952967
// SingleSignatureType
953968

954969
type SingleSignatureType struct {
@@ -1148,6 +1163,22 @@ type Signature struct {
11481163
composite *CompositeSignature
11491164
}
11501165

1166+
func (s *Signature) TypeParameters() []*Type {
1167+
return s.typeParameters
1168+
}
1169+
1170+
func (s *Signature) Declaration() *ast.Node {
1171+
return s.declaration
1172+
}
1173+
1174+
func (s *Signature) Target() *Signature {
1175+
return s.target
1176+
}
1177+
1178+
func (s *Signature) ThisParameter() *ast.Symbol {
1179+
return s.thisParameter
1180+
}
1181+
11511182
type CompositeSignature struct {
11521183
isUnion bool // True for union, false for intersection
11531184
signatures []*Signature // Individual signatures

0 commit comments

Comments
 (0)