Skip to content

Commit 008942f

Browse files
authored
Full-signature types for JSDoc (#1468)
1 parent e68e226 commit 008942f

File tree

90 files changed

+896
-827
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

90 files changed

+896
-827
lines changed

internal/ast/ast.go

Lines changed: 62 additions & 48 deletions
Large diffs are not rendered by default.

internal/ast/utilities.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3281,6 +3281,7 @@ func ReplaceModifiers(factory *NodeFactory, node *Node, modifierArray *ModifierL
32813281
node.TypeParameterList(),
32823282
node.ParameterList(),
32833283
node.Type(),
3284+
node.AsMethodDeclaration().FullSignature,
32843285
node.Body(),
32853286
)
32863287
case KindConstructor:
@@ -3290,6 +3291,7 @@ func ReplaceModifiers(factory *NodeFactory, node *Node, modifierArray *ModifierL
32903291
node.TypeParameterList(),
32913292
node.ParameterList(),
32923293
node.Type(),
3294+
node.AsConstructorDeclaration().FullSignature,
32933295
node.Body(),
32943296
)
32953297
case KindGetAccessor:
@@ -3300,6 +3302,7 @@ func ReplaceModifiers(factory *NodeFactory, node *Node, modifierArray *ModifierL
33003302
node.TypeParameterList(),
33013303
node.ParameterList(),
33023304
node.Type(),
3305+
node.AsGetAccessorDeclaration().FullSignature,
33033306
node.Body(),
33043307
)
33053308
case KindSetAccessor:
@@ -3310,6 +3313,7 @@ func ReplaceModifiers(factory *NodeFactory, node *Node, modifierArray *ModifierL
33103313
node.TypeParameterList(),
33113314
node.ParameterList(),
33123315
node.Type(),
3316+
node.AsSetAccessorDeclaration().FullSignature,
33133317
node.Body(),
33143318
)
33153319
case KindIndexSignature:
@@ -3328,6 +3332,7 @@ func ReplaceModifiers(factory *NodeFactory, node *Node, modifierArray *ModifierL
33283332
node.TypeParameterList(),
33293333
node.ParameterList(),
33303334
node.Type(),
3335+
node.AsFunctionExpression().FullSignature,
33313336
node.Body(),
33323337
)
33333338
case KindArrowFunction:
@@ -3337,6 +3342,7 @@ func ReplaceModifiers(factory *NodeFactory, node *Node, modifierArray *ModifierL
33373342
node.TypeParameterList(),
33383343
node.ParameterList(),
33393344
node.Type(),
3345+
node.AsArrowFunction().FullSignature,
33403346
node.AsArrowFunction().EqualsGreaterThanToken,
33413347
node.Body(),
33423348
)
@@ -3364,6 +3370,7 @@ func ReplaceModifiers(factory *NodeFactory, node *Node, modifierArray *ModifierL
33643370
node.TypeParameterList(),
33653371
node.ParameterList(),
33663372
node.Type(),
3373+
node.AsFunctionDeclaration().FullSignature,
33673374
node.Body(),
33683375
)
33693376
case KindClassDeclaration:

internal/checker/checker.go

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3241,6 +3241,14 @@ func (c *Checker) checkFunctionOrMethodDeclaration(node *ast.Node) {
32413241
body := node.Body()
32423242
c.checkSourceElement(body)
32433243
c.checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, c.getReturnTypeFromAnnotation(node))
3244+
if node.FunctionLikeData().FullSignature != nil {
3245+
if c.getContextualCallSignature(c.getTypeFromTypeNode(node.FunctionLikeData().FullSignature), node) == nil {
3246+
c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_on_a_function_must_have_a_signature_with_the_correct_number_of_arguments)
3247+
}
3248+
if node.Type() != nil || core.Some(node.Parameters(), func(p *ast.Node) bool { return p.Type() != nil }) {
3249+
c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_may_not_occur_with_a_param_or_returns_tag)
3250+
}
3251+
}
32443252
if node.Type() == nil {
32453253
// Report an implicit any error if there is no body, no explicit return type, and node is not a private method
32463254
// in an ambient context
@@ -3532,7 +3540,15 @@ func (c *Checker) checkAllCodePathsInNonVoidFunctionReturnOrThrow(fn *ast.Node,
35323540
return
35333541
}
35343542
hasExplicitReturn := fn.Flags&ast.NodeFlagsHasExplicitReturn != 0
3535-
errorNode := core.OrElse(fn.Type(), fn)
3543+
errorNode := fn.Type()
3544+
if errorNode == nil {
3545+
if data := fn.FunctionLikeData(); data != nil && data.FullSignature != nil {
3546+
errorNode = data.FullSignature
3547+
}
3548+
}
3549+
if errorNode == nil {
3550+
errorNode = fn
3551+
}
35363552
switch {
35373553
case t != nil && t.flags&TypeFlagsNever != 0:
35383554
c.error(errorNode, diagnostics.A_function_returning_never_cannot_have_a_reachable_end_point)
@@ -9754,6 +9770,14 @@ func (c *Checker) checkFunctionExpressionOrObjectLiteralMethod(node *ast.Node, c
97549770
if !hasGrammarError && ast.IsFunctionExpression(node) {
97559771
c.checkGrammarForGenerator(node)
97569772
}
9773+
if node.FunctionLikeData().FullSignature != nil {
9774+
if c.getContextualCallSignature(c.getTypeFromTypeNode(node.FunctionLikeData().FullSignature), node) == nil {
9775+
c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_on_a_function_must_have_a_signature_with_the_correct_number_of_arguments)
9776+
}
9777+
if node.Type() != nil || core.Some(node.Parameters(), func(p *ast.Node) bool { return p.Type() != nil }) {
9778+
c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_may_not_occur_with_a_param_or_returns_tag)
9779+
}
9780+
}
97579781
c.contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node, checkMode)
97589782
return c.getTypeOfSymbol(c.getSymbolOfDeclaration(node))
97599783
}
@@ -11593,6 +11617,11 @@ func (c *Checker) TryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, con
1159311617
}
1159411618
if ast.IsFunctionLike(container) && (!c.isInParameterInitializerBeforeContainingFunction(node) || ast.GetThisParameter(container) != nil) {
1159511619
thisType := c.getThisTypeOfDeclaration(container)
11620+
if thisType == nil && ast.IsInJSFile(container) {
11621+
if sig := c.getSignatureOfFullSignatureType(container); sig != nil {
11622+
thisType = c.getThisTypeOfSignature(sig)
11623+
}
11624+
}
1159611625
// Note: a parameter initializer should refer to class-this unless function-this is explicitly annotated.
1159711626
// If this is a function in a JS file, it might be a class method.
1159811627
if thisType == nil {
@@ -15935,6 +15964,9 @@ func (c *Checker) getTypeForVariableLikeDeclaration(declaration *ast.Node, inclu
1593515964
return c.getReturnTypeOfSignature(getterSignature)
1593615965
}
1593715966
}
15967+
if t := c.getParameterTypeOfFullSignature(fn, declaration); t != nil {
15968+
return t
15969+
}
1593815970
// Use contextual parameter type if one is available
1593915971
var t *Type
1594015972
if declaration.Symbol().Name == ast.InternalSymbolNameThis {
@@ -18906,6 +18938,12 @@ func (c *Checker) getSignaturesOfSymbol(symbol *ast.Symbol) []*Signature {
1890618938
}
1890718939
// If this is a function or method declaration, get the signature from the @type tag for the sake of optional parameters.
1890818940
// Exclude contextually-typed kinds because we already apply the @type tag to the context, plus applying it here to the initializer would suppress checks that the two are compatible.
18941+
if ast.IsFunctionExpressionOrArrowFunction(decl) || ast.IsObjectLiteralMethod(decl) {
18942+
if sig := c.getSignatureOfFullSignatureType(decl); sig != nil {
18943+
result = append(result, sig)
18944+
continue
18945+
}
18946+
}
1890918947
result = append(result, c.getSignatureFromDeclaration(decl))
1891018948
}
1891118949
return result
@@ -18984,6 +19022,11 @@ func (c *Checker) getTypeParametersFromDeclaration(declaration *ast.Node) []*Typ
1898419022
for _, node := range declaration.TypeParameters() {
1898519023
result = core.AppendIfUnique(result, c.getDeclaredTypeOfTypeParameter(node.Symbol()))
1898619024
}
19025+
if len(result) == 0 && ast.IsFunctionDeclaration(declaration) {
19026+
if sig := c.getSignatureOfFullSignatureType(declaration); sig != nil {
19027+
return sig.TypeParameters()
19028+
}
19029+
}
1898719030
return result
1898819031
}
1898919032

@@ -19133,6 +19176,32 @@ func (c *Checker) getReturnTypeFromAnnotation(declaration *ast.Node) *Type {
1913319176
if ast.IsGetAccessorDeclaration(declaration) && c.hasBindableName(declaration) {
1913419177
return c.getAnnotatedAccessorType(ast.GetDeclarationOfKind(c.getSymbolOfDeclaration(declaration), ast.KindSetAccessor))
1913519178
}
19179+
return c.getReturnTypeOfFullSignature(declaration)
19180+
}
19181+
19182+
func (c *Checker) getSignatureOfFullSignatureType(node *ast.Node) *Signature {
19183+
if ast.IsInJSFile(node) && ast.IsFunctionLike(node) && node.FunctionLikeData().FullSignature != nil {
19184+
return c.getSingleCallSignature(c.getTypeFromTypeNode(node.FunctionLikeData().FullSignature))
19185+
}
19186+
return nil
19187+
}
19188+
19189+
func (c *Checker) getParameterTypeOfFullSignature(node *ast.Node, parameter *ast.ParameterDeclarationNode) *Type {
19190+
if signature := c.getSignatureOfFullSignatureType(node); signature != nil {
19191+
pos := slices.Index(node.Parameters(), parameter)
19192+
if parameter.AsParameterDeclaration().DotDotDotToken != nil {
19193+
return c.getRestTypeAtPosition(signature, pos, false /*readonly*/)
19194+
} else {
19195+
return c.getTypeAtPosition(signature, pos)
19196+
}
19197+
}
19198+
return nil
19199+
}
19200+
19201+
func (c *Checker) getReturnTypeOfFullSignature(node *ast.Node) *Type {
19202+
if signature := c.getSignatureOfFullSignatureType(node); signature != nil {
19203+
return c.getReturnTypeOfSignature(signature)
19204+
}
1913619205
return nil
1913719206
}
1913819207

internal/checker/emitresolver.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,18 @@ func (r *emitResolver) CreateReturnTypeOfSignatureDeclaration(emitContext *print
863863
return requestNodeBuilder.SerializeReturnTypeForSignature(original, enclosingDeclaration, flags, internalFlags, tracker)
864864
}
865865

866+
func (r *emitResolver) CreateTypeParametersOfSignatureDeclaration(emitContext *printer.EmitContext, signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node {
867+
original := emitContext.ParseNode(signatureDeclaration)
868+
if original == nil {
869+
return nil
870+
}
871+
872+
r.checkerMu.Lock()
873+
defer r.checkerMu.Unlock()
874+
requestNodeBuilder := NewNodeBuilder(r.checker, emitContext) // TODO: cache per-context
875+
return requestNodeBuilder.SerializeTypeParametersForSignature(original, enclosingDeclaration, flags, internalFlags, tracker)
876+
}
877+
866878
func (r *emitResolver) CreateTypeOfDeclaration(emitContext *printer.EmitContext, declaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
867879
original := emitContext.ParseNode(declaration)
868880
if original == nil {

internal/checker/nodebuilder.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ func (b *NodeBuilder) SerializeReturnTypeForSignature(signatureDeclaration *ast.
9090
return b.exitContext(b.impl.serializeInferredReturnTypeForSignature(signature, returnType))
9191
}
9292

93+
func (b *NodeBuilder) SerializeTypeParametersForSignature(signatureDeclaration *ast.Node, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) []*ast.Node {
94+
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)
95+
symbol := b.impl.ch.getSymbolOfDeclaration(signatureDeclaration)
96+
typeParams := b.SymbolToTypeParameterDeclarations(symbol, enclosingDeclaration, flags, internalFlags, tracker)
97+
return b.exitContextSlice(typeParams)
98+
}
99+
93100
// SerializeTypeForDeclaration implements NodeBuilderInterface.
94101
func (b *NodeBuilder) SerializeTypeForDeclaration(declaration *ast.Node, symbol *ast.Symbol, enclosingDeclaration *ast.Node, flags nodebuilder.Flags, internalFlags nodebuilder.InternalFlags, tracker nodebuilder.SymbolTracker) *ast.Node {
95102
b.enterContext(enclosingDeclaration, flags, internalFlags, tracker)

internal/checker/nodebuilderimpl.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,12 @@ func (b *nodeBuilderImpl) typeParametersToTypeParameterDeclarations(symbol *ast.
15261526
results = append(results, b.typeParameterToDeclaration(param))
15271527
}
15281528
return results
1529+
} else if targetSymbol.Flags&ast.SymbolFlagsFunction != 0 {
1530+
var results []*ast.Node
1531+
for _, param := range b.ch.getTypeParametersFromDeclaration(symbol.ValueDeclaration) {
1532+
results = append(results, b.typeParameterToDeclaration(param))
1533+
}
1534+
return results
15291535
}
15301536
return nil
15311537
}
@@ -1760,13 +1766,13 @@ func (b *nodeBuilderImpl) signatureToSignatureDeclarationHelper(signature *Signa
17601766
}
17611767
node = b.f.NewMethodSignatureDeclaration(modifierList, name, questionToken, typeParamList, paramList, returnTypeNode)
17621768
case kind == ast.KindMethodDeclaration:
1763-
node = b.f.NewMethodDeclaration(modifierList, nil /*asteriskToken*/, name, nil /*questionToken*/, typeParamList, paramList, returnTypeNode, nil /*body*/)
1769+
node = b.f.NewMethodDeclaration(modifierList, nil /*asteriskToken*/, name, nil /*questionToken*/, typeParamList, paramList, returnTypeNode, nil /*fullSignature*/, nil /*body*/)
17641770
case kind == ast.KindConstructor:
1765-
node = b.f.NewConstructorDeclaration(modifierList, nil /*typeParamList*/, paramList, nil /*returnTypeNode*/, nil /*body*/)
1771+
node = b.f.NewConstructorDeclaration(modifierList, nil /*typeParamList*/, paramList, nil /*returnTypeNode*/, nil /*fullSignature*/, nil /*body*/)
17661772
case kind == ast.KindGetAccessor:
1767-
node = b.f.NewGetAccessorDeclaration(modifierList, name, nil /*typeParamList*/, paramList, returnTypeNode, nil /*body*/)
1773+
node = b.f.NewGetAccessorDeclaration(modifierList, name, nil /*typeParamList*/, paramList, returnTypeNode, nil /*fullSignature*/, nil /*body*/)
17681774
case kind == ast.KindSetAccessor:
1769-
node = b.f.NewSetAccessorDeclaration(modifierList, name, nil /*typeParamList*/, paramList, nil /*returnTypeNode*/, nil /*body*/)
1775+
node = b.f.NewSetAccessorDeclaration(modifierList, name, nil /*typeParamList*/, paramList, nil /*returnTypeNode*/, nil /*fullSignature*/, nil /*body*/)
17701776
case kind == ast.KindIndexSignature:
17711777
node = b.f.NewIndexSignatureDeclaration(modifierList, paramList, returnTypeNode)
17721778
// !!! JSDoc Support
@@ -1784,12 +1790,12 @@ func (b *nodeBuilderImpl) signatureToSignatureDeclarationHelper(signature *Signa
17841790
node = b.f.NewConstructorTypeNode(modifierList, typeParamList, paramList, returnTypeNode)
17851791
case kind == ast.KindFunctionDeclaration:
17861792
// TODO: assert name is Identifier
1787-
node = b.f.NewFunctionDeclaration(modifierList, nil /*asteriskToken*/, name, typeParamList, paramList, returnTypeNode, nil /*body*/)
1793+
node = b.f.NewFunctionDeclaration(modifierList, nil /*asteriskToken*/, name, typeParamList, paramList, returnTypeNode, nil /*fullSignature*/, nil /*body*/)
17881794
case kind == ast.KindFunctionExpression:
17891795
// TODO: assert name is Identifier
1790-
node = b.f.NewFunctionExpression(modifierList, nil /*asteriskToken*/, name, typeParamList, paramList, returnTypeNode, b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false))
1796+
node = b.f.NewFunctionExpression(modifierList, nil /*asteriskToken*/, name, typeParamList, paramList, returnTypeNode, nil /*fullSignature*/, b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false))
17911797
case kind == ast.KindArrowFunction:
1792-
node = b.f.NewArrowFunction(modifierList, typeParamList, paramList, returnTypeNode, nil /*equalsGreaterThanToken*/, b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false))
1798+
node = b.f.NewArrowFunction(modifierList, typeParamList, paramList, returnTypeNode, nil /*fullSignature*/, nil /*equalsGreaterThanToken*/, b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false))
17931799
default:
17941800
panic("Unhandled kind in signatureToSignatureDeclarationHelper")
17951801
}

internal/checker/relater.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1999,11 +1999,19 @@ func (c *Checker) getTypePredicateOfSignature(sig *Signature) *TypePredicate {
19991999
default:
20002000
if sig.declaration != nil {
20012001
typeNode := sig.declaration.Type()
2002+
var jsdocTypePredicate *TypePredicate
2003+
if typeNode == nil {
2004+
if jsdocSignature := c.getSignatureOfFullSignatureType(sig.declaration); jsdocSignature != nil {
2005+
jsdocTypePredicate = c.getTypePredicateOfSignature(jsdocSignature)
2006+
}
2007+
}
20022008
switch {
20032009
case typeNode != nil:
20042010
if ast.IsTypePredicateNode(typeNode) {
20052011
sig.resolvedTypePredicate = c.createTypePredicateFromTypePredicateNode(typeNode, sig)
20062012
}
2013+
case jsdocTypePredicate != nil:
2014+
sig.resolvedTypePredicate = jsdocTypePredicate
20072015
case ast.IsFunctionLikeDeclaration(sig.declaration) && (sig.resolvedReturnType == nil || sig.resolvedReturnType.flags&TypeFlagsBoolean != 0) && c.getParameterCount(sig) > 0:
20082016
sig.resolvedTypePredicate = c.noTypePredicate // avoid infinite loop
20092017
sig.resolvedTypePredicate = c.getTypePredicateFromBody(sig.declaration)

internal/diagnostics/diagnostics_generated.go

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/diagnostics/extraDiagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,13 @@
1414
"Non-relative paths are not allowed. Did you forget a leading './'?": {
1515
"category": "Error",
1616
"code": 5090
17+
},
18+
"A JSDoc '@type' tag on a function must have a signature with the correct number of arguments.": {
19+
"category": "Error",
20+
"code": 8030
21+
},
22+
"A JSDoc '@type' tag may not occur with a '@param' or '@returns' tag.": {
23+
"category": "Error",
24+
"code": 8040
1725
}
1826
}

0 commit comments

Comments
 (0)