749 lines
29 KiB
Go
749 lines
29 KiB
Go
package parser
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/microsoft/typescript-go/internal/ast"
|
|
"github.com/microsoft/typescript-go/internal/core"
|
|
"github.com/microsoft/typescript-go/internal/diagnostics"
|
|
"github.com/microsoft/typescript-go/internal/scanner"
|
|
)
|
|
|
|
func (p *Parser) finishReparsedNode(node *ast.Node, locationNode *ast.Node) {
|
|
node.Flags = p.contextFlags | ast.NodeFlagsReparsed
|
|
node.Loc = locationNode.Loc
|
|
p.overrideParentInImmediateChildren(node)
|
|
}
|
|
|
|
func (p *Parser) finishMutatedNode(node *ast.Node) {
|
|
p.overrideParentInImmediateChildren(node)
|
|
}
|
|
|
|
// Deep-clone the given node and add the clone to the reparsed clone list. The list is used by ast.GetReparsedNodeForNode
|
|
// to locate reparsed clones of JSDoc nodes. Since the binder attaches symbols to reparsed nodes and not to JSDoc nodes, we
|
|
// need the mapping when obtaining symbols and types from JSDoc nodes.
|
|
func (p *Parser) addDeepCloneReparse(node *ast.Node) *ast.Node {
|
|
clone := p.factory.DeepCloneReparse(node)
|
|
if clone != nil {
|
|
p.reparsedClones = append(p.reparsedClones, clone)
|
|
}
|
|
return clone
|
|
}
|
|
|
|
func (p *Parser) addTransformedReparse(newNode *ast.Node, old *ast.Node) *ast.Node {
|
|
p.finishReparsedNode(newNode, old)
|
|
newNode.Flags |= ast.NodeFlagsReparserTransformedLiteral
|
|
p.reparsedClones = append(p.reparsedClones, newNode)
|
|
return newNode
|
|
}
|
|
|
|
func (p *Parser) checkNonIdentifierName(name *ast.Node) *ast.Node {
|
|
if ast.IsIdentifier(name) && !scanner.IsValidIdentifier(name.AsIdentifier().Text) {
|
|
errLoc := name.Loc
|
|
if errLoc.Len() == 0 { // missing name, emit error on the character before the missing name node
|
|
errLoc = core.NewTextRange(name.Loc.Pos()-1, name.Loc.Pos())
|
|
}
|
|
p.parseErrorAtRange(errLoc, diagnostics.Identifier_expected)
|
|
}
|
|
return name
|
|
}
|
|
|
|
// Hosted tags find a host and add their children to the correct location under the host.
|
|
// Unhosted tags add synthetic nodes to the reparse list.
|
|
func (p *Parser) reparseTags(parent *ast.Node, jsDoc []*ast.Node) {
|
|
for _, j := range jsDoc {
|
|
isLast := j == jsDoc[len(jsDoc)-1]
|
|
tags := j.AsJSDoc().Tags
|
|
if tags == nil {
|
|
continue
|
|
}
|
|
for _, tag := range tags.Nodes {
|
|
p.reparseUnhosted(tag, parent, j)
|
|
if isLast {
|
|
p.reparseHosted(tag, parent, j)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Parser) reparseUnhosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node) {
|
|
switch tag.Kind {
|
|
case ast.KindJSDocTypedefTag:
|
|
typeExpression := tag.TypeExpression()
|
|
if typeExpression == nil {
|
|
break
|
|
}
|
|
fullName := tag.Name()
|
|
isNamespace := fullName != nil && ast.IsModuleDeclaration(fullName)
|
|
var modifiers *ast.ModifierList
|
|
if isNamespace {
|
|
modifiers = p.createExportModifier(tag)
|
|
}
|
|
typeAlias := p.factory.NewJSTypeAliasDeclaration(modifiers, p.addDeepCloneReparse(p.checkNonIdentifierName(p.getInnermostNameOfJSDocNamespace(fullName))), nil, nil)
|
|
typeAlias.AsTypeAliasDeclaration().TypeParameters = p.gatherTypeParameters(jsDoc, true /*typedefOrCallback*/)
|
|
var t *ast.Node
|
|
switch typeExpression.Kind {
|
|
case ast.KindJSDocTypeExpression:
|
|
t = p.addDeepCloneReparse(typeExpression.Type())
|
|
case ast.KindJSDocTypeLiteral:
|
|
t = p.reparseJSDocTypeLiteral(typeExpression)
|
|
default:
|
|
panic("typedef tag type expression should be a name reference or a type expression" + typeExpression.Kind.String())
|
|
}
|
|
typeAlias.AsTypeAliasDeclaration().Type = t
|
|
p.finishReparsedNode(typeAlias, tag)
|
|
p.jsdocInfos = append(p.jsdocInfos, JSDocInfo{parent: typeAlias, jsDocs: []*ast.Node{jsDoc}})
|
|
typeAlias.Flags |= ast.NodeFlagsHasJSDoc
|
|
result := p.wrapInJSDocNamespace(fullName, typeAlias, false /*nested*/)
|
|
p.reparseList = append(p.reparseList, result)
|
|
case ast.KindJSDocCallbackTag:
|
|
typeExpression := tag.TypeExpression()
|
|
if typeExpression == nil {
|
|
break
|
|
}
|
|
fullName := tag.Name()
|
|
isNamespace := fullName != nil && ast.IsModuleDeclaration(fullName)
|
|
var modifiers *ast.ModifierList
|
|
if isNamespace {
|
|
modifiers = p.createExportModifier(tag)
|
|
}
|
|
functionType := p.reparseJSDocSignature(typeExpression, tag, jsDoc, tag, nil)
|
|
typeAlias := p.factory.NewJSTypeAliasDeclaration(modifiers, p.addDeepCloneReparse(p.getInnermostNameOfJSDocNamespace(fullName)), nil, functionType)
|
|
typeAlias.AsTypeAliasDeclaration().TypeParameters = p.gatherTypeParameters(jsDoc, true /*typedefOrCallback*/)
|
|
p.finishReparsedNode(typeAlias, tag)
|
|
p.jsdocInfos = append(p.jsdocInfos, JSDocInfo{parent: typeAlias, jsDocs: []*ast.Node{jsDoc}})
|
|
typeAlias.Flags |= ast.NodeFlagsHasJSDoc
|
|
result := p.wrapInJSDocNamespace(fullName, typeAlias, false /*nested*/)
|
|
p.reparseList = append(p.reparseList, result)
|
|
case ast.KindJSDocImportTag:
|
|
importTag := tag.AsJSDocImportTag()
|
|
if importTag.ImportClause == nil {
|
|
break
|
|
}
|
|
importClause := p.addDeepCloneReparse(importTag.ImportClause)
|
|
importClause.AsImportClause().PhaseModifier = ast.KindTypeKeyword
|
|
importDeclaration := p.factory.NewJSImportDeclaration(
|
|
p.factory.DeepCloneReparseModifiers(importTag.Modifiers()),
|
|
importClause,
|
|
p.addDeepCloneReparse(importTag.ModuleSpecifier),
|
|
p.addDeepCloneReparse(importTag.Attributes),
|
|
)
|
|
p.finishReparsedNode(importDeclaration, tag)
|
|
p.reparseList = append(p.reparseList, importDeclaration)
|
|
case ast.KindJSDocOverloadTag:
|
|
// Create overload signatures only for function, method, and constructor declarations outside object literals
|
|
if (ast.IsFunctionDeclaration(parent) || ast.IsMethodDeclaration(parent) || ast.IsConstructorDeclaration(parent)) && p.parsingContexts&(1<<PCObjectLiteralMembers) == 0 {
|
|
p.reparseList = append(p.reparseList, p.reparseJSDocSignature(tag.AsJSDocOverloadTag().TypeExpression, parent, jsDoc, tag, parent.Modifiers()))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Parser) reparseJSDocSignature(jsSignature *ast.Node, fun *ast.Node, jsDoc *ast.Node, tag *ast.Node, modifiers *ast.ModifierList) *ast.Node {
|
|
var signature *ast.Node
|
|
clonedModifiers := p.factory.DeepCloneReparseModifiers(modifiers)
|
|
switch fun.Kind {
|
|
case ast.KindFunctionDeclaration:
|
|
signature = p.factory.NewFunctionDeclaration(clonedModifiers, nil, p.factory.DeepCloneReparse(p.checkNonIdentifierName(fun.Name())), nil, nil, nil, nil, nil)
|
|
case ast.KindMethodDeclaration:
|
|
signature = p.factory.NewMethodDeclaration(clonedModifiers, nil, p.factory.DeepCloneReparse(p.checkNonIdentifierName(fun.Name())), nil, nil, nil, nil, nil, nil)
|
|
case ast.KindConstructor:
|
|
signature = p.factory.NewConstructorDeclaration(clonedModifiers, nil, nil, nil, nil, nil)
|
|
case ast.KindJSDocCallbackTag:
|
|
signature = p.factory.NewFunctionTypeNode(nil, nil, p.factory.NewKeywordTypeNode(ast.KindAnyKeyword))
|
|
default:
|
|
panic("Unexpected kind " + fun.Kind.String())
|
|
}
|
|
|
|
if tag.Kind != ast.KindJSDocCallbackTag {
|
|
signature.FunctionLikeData().TypeParameters = p.gatherTypeParameters(jsDoc, false /*typedefOrCallback*/)
|
|
}
|
|
parameters := p.nodeSliceArena.NewSlice(0)
|
|
for pi, param := range jsSignature.Parameters() {
|
|
var parameter *ast.Node
|
|
if param.Kind == ast.KindJSDocThisTag {
|
|
thisTag := param.AsJSDocThisTag()
|
|
thisIdent := p.factory.NewIdentifier("this")
|
|
thisIdent.Loc = thisTag.Loc
|
|
thisIdent.Flags = p.contextFlags | ast.NodeFlagsReparsed
|
|
parameter = p.factory.NewParameterDeclaration(nil, nil, thisIdent, nil, nil, nil)
|
|
if thisTag.TypeExpression != nil {
|
|
parameter.AsParameterDeclaration().Type = p.addDeepCloneReparse(thisTag.TypeExpression.Type())
|
|
}
|
|
} else if param.Kind == ast.KindJSDocParameterTag || param.Kind == ast.KindJSDocPropertyTag {
|
|
jsparam := param.AsJSDocParameterOrPropertyTag()
|
|
// Skip sub-property parameters (e.g., @param x.y) - these have QualifiedNames
|
|
// and describe properties of a parent parameter, not standalone parameters.
|
|
if ast.IsQualifiedName(jsparam.Name()) {
|
|
continue
|
|
}
|
|
var dotDotDotToken *ast.Node
|
|
var paramType *ast.TypeNode
|
|
|
|
if jsparam.TypeExpression != nil {
|
|
if jsparam.TypeExpression.Type().Kind == ast.KindJSDocVariadicType {
|
|
dotDotDotToken = p.factory.NewToken(ast.KindDotDotDotToken)
|
|
dotDotDotToken.Loc = jsparam.Loc
|
|
dotDotDotToken.Flags = p.contextFlags | ast.NodeFlagsReparsed
|
|
|
|
variadicType := jsparam.TypeExpression.Type().AsJSDocVariadicType()
|
|
paramType = p.reparseJSDocTypeLiteral(variadicType.Type)
|
|
} else {
|
|
paramType = p.reparseJSDocTypeLiteral(jsparam.TypeExpression.Type())
|
|
}
|
|
}
|
|
name := jsparam.Name()
|
|
if ast.IsIdentifier(name) && !scanner.IsValidIdentifier(name.AsIdentifier().Text) {
|
|
// drop invalid chars for _, if empty, write _0, etc., so we have a valid param name to emit later
|
|
result := strings.Builder{}
|
|
for i, ch := range name.AsIdentifier().Text {
|
|
if i == 0 {
|
|
if !scanner.IsIdentifierStart(ch) {
|
|
result.WriteRune('_')
|
|
} else {
|
|
result.WriteRune(ch)
|
|
}
|
|
continue
|
|
} else if !scanner.IsIdentifierPart(ch) {
|
|
result.WriteRune('_')
|
|
} else {
|
|
result.WriteRune(ch)
|
|
}
|
|
}
|
|
if result.Len() == 0 {
|
|
result.WriteRune('_')
|
|
result.WriteString(strconv.Itoa(pi))
|
|
}
|
|
name = p.addTransformedReparse(p.factory.NewIdentifier(result.String()), name)
|
|
} else {
|
|
name = p.addDeepCloneReparse(name)
|
|
}
|
|
parameter = p.factory.NewParameterDeclaration(nil, dotDotDotToken, name, p.makeQuestionIfOptional(jsparam), paramType, nil)
|
|
}
|
|
p.finishReparsedNode(parameter, param)
|
|
parameters = append(parameters, parameter)
|
|
p.reparseJSDocComment(parameter, param)
|
|
}
|
|
signature.FunctionLikeData().Parameters = p.newNodeList(jsSignature.AsJSDocSignature().Parameters.Loc, parameters)
|
|
|
|
if jsSignature.Type() != nil && jsSignature.Type().TypeExpression() != nil {
|
|
signature.FunctionLikeData().Type = p.addDeepCloneReparse(jsSignature.Type().TypeExpression().Type())
|
|
}
|
|
loc := jsSignature
|
|
if tag.Kind == ast.KindJSDocOverloadTag {
|
|
loc = tag.TagName()
|
|
}
|
|
p.finishReparsedNode(signature, loc)
|
|
return signature
|
|
}
|
|
|
|
func (p *Parser) reparseJSDocTypeLiteral(t *ast.TypeNode) *ast.Node {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
if t.Kind == ast.KindJSDocTypeLiteral {
|
|
jstypeliteral := t.AsJSDocTypeLiteral()
|
|
isArrayType := jstypeliteral.IsArrayType
|
|
properties := p.nodeSliceArena.NewSlice(0)
|
|
for _, prop := range jstypeliteral.JSDocPropertyTags {
|
|
if prop.Kind != ast.KindJSDocPropertyTag && prop.Kind != ast.KindJSDocParameterTag {
|
|
continue
|
|
}
|
|
jsprop := prop.AsJSDocParameterOrPropertyTag()
|
|
name := prop.Name()
|
|
if name.Kind == ast.KindQualifiedName {
|
|
name = name.AsQualifiedName().Right
|
|
}
|
|
if ast.IsIdentifier(name) && !scanner.IsValidIdentifier(name.AsIdentifier().Text) {
|
|
name = p.addTransformedReparse(p.factory.NewStringLiteral(name.AsIdentifier().Text, ast.TokenFlagsNone), name)
|
|
} else {
|
|
name = p.addDeepCloneReparse(name)
|
|
}
|
|
property := p.factory.NewPropertySignatureDeclaration(nil, name, p.makeQuestionIfOptional(jsprop), nil, nil)
|
|
if jsprop.TypeExpression != nil {
|
|
property.AsPropertySignatureDeclaration().Type = p.reparseJSDocTypeLiteral(jsprop.TypeExpression.Type())
|
|
}
|
|
p.finishReparsedNode(property, prop)
|
|
properties = append(properties, property)
|
|
p.reparseJSDocComment(property, prop)
|
|
}
|
|
t = p.factory.NewTypeLiteralNode(p.newNodeList(jstypeliteral.Loc, properties))
|
|
if isArrayType {
|
|
p.finishReparsedNode(t, jstypeliteral.AsNode())
|
|
t = p.factory.NewArrayTypeNode(t)
|
|
}
|
|
p.finishReparsedNode(t, jstypeliteral.AsNode())
|
|
return t
|
|
}
|
|
return p.addDeepCloneReparse(t)
|
|
}
|
|
|
|
func (p *Parser) reparseJSDocComment(node *ast.Node, tag *ast.Node) {
|
|
if comment := tag.CommentList(); comment != nil {
|
|
newComment := p.factory.NewNodeList(core.Map(comment.Nodes, p.factory.DeepCloneReparse))
|
|
newComment.Loc = comment.Loc
|
|
propJSDoc := p.factory.NewJSDoc(newComment, nil)
|
|
p.finishReparsedNode(propJSDoc, tag)
|
|
propJSDoc.Parent = node
|
|
p.jsdocInfos = append(p.jsdocInfos, JSDocInfo{parent: node, jsDocs: []*ast.Node{propJSDoc}})
|
|
node.Flags |= ast.NodeFlagsHasJSDoc
|
|
}
|
|
}
|
|
|
|
func (p *Parser) gatherTypeParameters(j *ast.Node, typedefOrCallback bool) *ast.NodeList {
|
|
var typeParameters []*ast.Node
|
|
pos := -1
|
|
endPos := -1
|
|
firstTemplate := true
|
|
for _, tag := range j.AsJSDoc().Tags.Nodes {
|
|
// When a JSDoc comment contains an `@typedef` or `@callback` tag, `@template` type parameter
|
|
// declarations apply to the type being defined.
|
|
if !typedefOrCallback && (ast.IsJSDocTypedefTag(tag) || ast.IsJSDocCallbackTag(tag)) {
|
|
return nil
|
|
}
|
|
if !ast.IsJSDocTemplateTag(tag) {
|
|
continue
|
|
}
|
|
if firstTemplate {
|
|
pos = tag.Pos()
|
|
firstTemplate = false
|
|
}
|
|
endPos = tag.End()
|
|
constraint := tag.AsJSDocTemplateTag().Constraint
|
|
firstTypeParameter := true
|
|
for _, tp := range tag.TypeParameters() {
|
|
var reparse *ast.Node
|
|
if constraint != nil && firstTypeParameter {
|
|
reparse = p.factory.NewTypeParameterDeclaration(
|
|
p.factory.DeepCloneReparseModifiers(tp.Modifiers()),
|
|
p.addDeepCloneReparse(p.checkNonIdentifierName(tp.Name())),
|
|
p.addDeepCloneReparse(constraint.Type()),
|
|
nil, // expression
|
|
p.addDeepCloneReparse(tp.AsTypeParameterDeclaration().DefaultType),
|
|
)
|
|
p.finishReparsedNode(reparse, tp)
|
|
} else {
|
|
reparse = p.addDeepCloneReparse(tp)
|
|
}
|
|
if typeParameters == nil {
|
|
typeParameters = p.nodeSliceArena.NewSlice(0)
|
|
}
|
|
typeParameters = append(typeParameters, reparse)
|
|
firstTypeParameter = false
|
|
}
|
|
}
|
|
if len(typeParameters) == 0 {
|
|
return nil
|
|
} else {
|
|
return p.newNodeList(core.NewTextRange(pos, endPos), typeParameters)
|
|
}
|
|
}
|
|
|
|
func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node) {
|
|
switch tag.Kind {
|
|
case ast.KindJSDocTypeTag:
|
|
switch parent.Kind {
|
|
case ast.KindVariableStatement:
|
|
if parent.AsVariableStatement().DeclarationList != nil {
|
|
for _, declaration := range parent.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
|
|
if declaration.Type() == nil && tag.TypeExpression() != nil {
|
|
declaration.AsMutable().SetType(p.addDeepCloneReparse(tag.TypeExpression().Type()))
|
|
p.finishMutatedNode(declaration)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
case ast.KindVariableDeclaration, ast.KindExportAssignment, ast.KindPropertyDeclaration, ast.KindPropertyAssignment,
|
|
ast.KindShorthandPropertyAssignment, ast.KindGetAccessor:
|
|
if parent.Type() == nil && tag.TypeExpression() != nil {
|
|
parent.AsMutable().SetType(p.addDeepCloneReparse(tag.TypeExpression().Type()))
|
|
p.finishMutatedNode(parent)
|
|
return
|
|
}
|
|
case ast.KindParameter:
|
|
if parent.Type() == nil && tag.TypeExpression() != nil {
|
|
parent.AsMutable().SetType(p.reparseJSDocTypeLiteral(tag.TypeExpression().Type()))
|
|
p.finishMutatedNode(parent)
|
|
return
|
|
}
|
|
case ast.KindExpressionStatement:
|
|
if parent.Expression().Kind == ast.KindBinaryExpression {
|
|
bin := parent.Expression().AsBinaryExpression()
|
|
if kind := ast.GetAssignmentDeclarationKind(bin.AsNode()); kind != ast.JSDeclarationKindNone && tag.TypeExpression() != nil {
|
|
bin.AsMutable().SetType(p.addDeepCloneReparse(tag.TypeExpression().Type()))
|
|
p.finishMutatedNode(bin.AsNode())
|
|
return
|
|
}
|
|
}
|
|
case ast.KindReturnStatement, ast.KindParenthesizedExpression:
|
|
if parent.Expression() != nil && tag.TypeExpression() != nil {
|
|
parent.AsMutable().SetExpression(p.makeNewCast(
|
|
p.addDeepCloneReparse(tag.TypeExpression().Type()),
|
|
parent.Expression(),
|
|
true, /*isAssertion*/
|
|
))
|
|
p.finishMutatedNode(parent)
|
|
return
|
|
}
|
|
}
|
|
if fun := getFunctionLikeHost(parent); fun != nil {
|
|
noTypedParams := core.Every(fun.Parameters(), func(param *ast.Node) bool { return param.Type() == nil })
|
|
if fun.TypeParameterList() == nil && fun.Type() == nil && noTypedParams && tag.TypeExpression() != nil {
|
|
fun.FunctionLikeData().FullSignature = p.addDeepCloneReparse(tag.TypeExpression().Type())
|
|
p.finishMutatedNode(fun)
|
|
}
|
|
}
|
|
case ast.KindJSDocSatisfiesTag:
|
|
switch parent.Kind {
|
|
case ast.KindVariableStatement:
|
|
if parent.AsVariableStatement().DeclarationList != nil {
|
|
for _, declaration := range parent.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
|
|
if declaration.Initializer() != nil && tag.TypeExpression() != nil {
|
|
declaration.AsMutable().SetInitializer(p.makeNewCast(
|
|
p.addDeepCloneReparse(tag.TypeExpression().Type()),
|
|
declaration.Initializer(),
|
|
false, /*isAssertion*/
|
|
))
|
|
p.finishMutatedNode(declaration)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
case ast.KindVariableDeclaration, ast.KindPropertyDeclaration, ast.KindPropertyAssignment:
|
|
if parent.Initializer() != nil && tag.TypeExpression() != nil {
|
|
parent.AsMutable().SetInitializer(p.makeNewCast(
|
|
p.addDeepCloneReparse(tag.TypeExpression().Type()),
|
|
parent.Initializer(),
|
|
false, /*isAssertion*/
|
|
))
|
|
p.finishMutatedNode(parent)
|
|
}
|
|
case ast.KindShorthandPropertyAssignment:
|
|
shorthand := parent.AsShorthandPropertyAssignment()
|
|
if shorthand.ObjectAssignmentInitializer != nil && tag.AsJSDocSatisfiesTag().TypeExpression != nil {
|
|
shorthand.ObjectAssignmentInitializer = p.makeNewCast(
|
|
p.addDeepCloneReparse(tag.AsJSDocSatisfiesTag().TypeExpression.Type()),
|
|
shorthand.ObjectAssignmentInitializer,
|
|
false, /*isAssertion*/
|
|
)
|
|
p.finishMutatedNode(parent)
|
|
}
|
|
case ast.KindReturnStatement, ast.KindParenthesizedExpression, ast.KindExportAssignment:
|
|
if parent.Expression() != nil && tag.TypeExpression() != nil {
|
|
parent.AsMutable().SetExpression(p.makeNewCast(
|
|
p.addDeepCloneReparse(tag.TypeExpression().Type()),
|
|
parent.Expression(),
|
|
false, /*isAssertion*/
|
|
))
|
|
p.finishMutatedNode(parent)
|
|
}
|
|
case ast.KindExpressionStatement:
|
|
if parent.Expression().Kind == ast.KindBinaryExpression {
|
|
bin := parent.Expression().AsBinaryExpression()
|
|
if kind := ast.GetAssignmentDeclarationKind(bin.AsNode()); kind != ast.JSDeclarationKindNone && tag.TypeExpression() != nil {
|
|
bin.Right = p.makeNewCast(
|
|
p.addDeepCloneReparse(tag.TypeExpression().Type()),
|
|
bin.Right,
|
|
false, /*isAssertion*/
|
|
)
|
|
p.finishMutatedNode(bin.AsNode())
|
|
}
|
|
}
|
|
}
|
|
case ast.KindJSDocTemplateTag:
|
|
if fun := getFunctionLikeHost(parent); fun != nil {
|
|
if fun.TypeParameters() == nil && fun.FunctionLikeData().FullSignature == nil {
|
|
fun.FunctionLikeData().TypeParameters = p.gatherTypeParameters(jsDoc, false /*typedefOrCallback*/)
|
|
p.finishMutatedNode(fun)
|
|
}
|
|
} else if parent.Kind == ast.KindClassDeclaration {
|
|
class := parent.AsClassDeclaration()
|
|
if class.TypeParameters == nil {
|
|
class.TypeParameters = p.gatherTypeParameters(jsDoc, false /*typedefOrCallback*/)
|
|
p.finishMutatedNode(parent)
|
|
}
|
|
} else if parent.Kind == ast.KindClassExpression {
|
|
class := parent.AsClassExpression()
|
|
if class.TypeParameters == nil {
|
|
class.TypeParameters = p.gatherTypeParameters(jsDoc, false /*typedefOrCallback*/)
|
|
p.finishMutatedNode(parent)
|
|
}
|
|
}
|
|
case ast.KindJSDocParameterTag:
|
|
if fun := getFunctionLikeHost(parent); fun != nil && fun.FunctionLikeData().FullSignature == nil {
|
|
parameterTag := tag.AsJSDocParameterOrPropertyTag()
|
|
if param, ok := findMatchingParameter(fun, parameterTag, jsDoc); ok {
|
|
if param.Type == nil && parameterTag.TypeExpression != nil {
|
|
param.AsParameterDeclaration().Type = p.reparseJSDocTypeLiteral(parameterTag.TypeExpression.Type())
|
|
}
|
|
if param.QuestionToken == nil {
|
|
if question := p.makeQuestionIfOptional(parameterTag); question != nil {
|
|
param.QuestionToken = question
|
|
}
|
|
}
|
|
p.finishMutatedNode(param.AsNode())
|
|
}
|
|
}
|
|
case ast.KindJSDocThisTag:
|
|
if fun := getFunctionLikeHost(parent); fun != nil {
|
|
params := fun.Parameters()
|
|
if len(params) == 0 || (params[0].Name().Kind != ast.KindThisKeyword && !ast.IsThisIdentifier(params[0].Name())) {
|
|
thisParam := p.factory.NewParameterDeclaration(
|
|
nil, /* decorators */
|
|
nil, /* modifiers */
|
|
p.factory.NewIdentifier("this"),
|
|
nil, /* questionToken */
|
|
nil, /* type */
|
|
nil, /* initializer */
|
|
)
|
|
if tag.AsJSDocThisTag().TypeExpression != nil {
|
|
thisParam.AsParameterDeclaration().Type = p.addDeepCloneReparse(tag.AsJSDocThisTag().TypeExpression.Type())
|
|
}
|
|
p.finishReparsedNode(thisParam, tag.TagName())
|
|
|
|
newParams := p.nodeSliceArena.NewSlice(len(params) + 1)
|
|
newParams[0] = thisParam
|
|
for i, param := range params {
|
|
newParams[i+1] = param
|
|
}
|
|
|
|
fun.FunctionLikeData().Parameters = p.newNodeList(fun.ParameterList().Loc, newParams)
|
|
p.finishMutatedNode(fun)
|
|
}
|
|
}
|
|
case ast.KindJSDocReturnTag:
|
|
if fun := getFunctionLikeHost(parent); fun != nil && fun.FunctionLikeData().FullSignature == nil {
|
|
if fun.Type() == nil && tag.TypeExpression() != nil {
|
|
fun.FunctionLikeData().Type = p.addDeepCloneReparse(tag.TypeExpression().Type())
|
|
p.finishMutatedNode(fun)
|
|
}
|
|
}
|
|
case ast.KindJSDocReadonlyTag, ast.KindJSDocPrivateTag, ast.KindJSDocPublicTag, ast.KindJSDocProtectedTag, ast.KindJSDocOverrideTag:
|
|
if parent.Kind == ast.KindExpressionStatement {
|
|
parent = parent.Expression()
|
|
}
|
|
switch parent.Kind {
|
|
case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
|
|
// In object literals these aren't class-like members, so JSDoc modifiers like @override
|
|
// or @readonly aren't real modifiers there; reparsing them produces spurious grammar errors (#4437).
|
|
if p.parsingContexts&(1<<PCObjectLiteralMembers) != 0 {
|
|
return
|
|
}
|
|
fallthrough
|
|
case ast.KindPropertyDeclaration, ast.KindConstructor, ast.KindBinaryExpression:
|
|
var keyword ast.Kind
|
|
switch tag.Kind {
|
|
case ast.KindJSDocReadonlyTag:
|
|
keyword = ast.KindReadonlyKeyword
|
|
case ast.KindJSDocPrivateTag:
|
|
keyword = ast.KindPrivateKeyword
|
|
case ast.KindJSDocPublicTag:
|
|
keyword = ast.KindPublicKeyword
|
|
case ast.KindJSDocProtectedTag:
|
|
keyword = ast.KindProtectedKeyword
|
|
case ast.KindJSDocOverrideTag:
|
|
keyword = ast.KindOverrideKeyword
|
|
}
|
|
modifier := p.factory.NewModifier(keyword)
|
|
modifier.Loc = tag.Loc
|
|
modifier.Flags = p.contextFlags | ast.NodeFlagsReparsed
|
|
var nodes []*ast.Node
|
|
var loc core.TextRange
|
|
if parent.Modifiers() == nil {
|
|
nodes = p.nodeSliceArena.NewSlice(1)
|
|
nodes[0] = modifier
|
|
loc = tag.Loc
|
|
} else {
|
|
nodes = append(parent.ModifierNodes(), modifier)
|
|
loc = parent.Modifiers().Loc
|
|
}
|
|
parent.AsMutable().SetModifiers(p.newModifierList(loc, nodes))
|
|
p.finishMutatedNode(parent)
|
|
}
|
|
case ast.KindJSDocImplementsTag:
|
|
if class := getClassLikeData(parent); class != nil {
|
|
implementsTag := tag.AsJSDocImplementsTag()
|
|
|
|
if class.HeritageClauses != nil {
|
|
if implementsClause := core.Find(class.HeritageClauses.Nodes, func(node *ast.Node) bool {
|
|
return node.AsHeritageClause().Token == ast.KindImplementsKeyword
|
|
}); implementsClause != nil {
|
|
implementsClause.AsHeritageClause().Types.Nodes = append(implementsClause.AsHeritageClause().Types.Nodes, p.addDeepCloneReparse(implementsTag.ClassName))
|
|
p.finishMutatedNode(implementsClause)
|
|
return
|
|
}
|
|
}
|
|
typesList := p.newNodeList(implementsTag.ClassName.Loc, p.nodeSliceArena.NewSlice1(p.addDeepCloneReparse(implementsTag.ClassName)))
|
|
|
|
heritageClause := p.factory.NewHeritageClause(ast.KindImplementsKeyword, typesList)
|
|
p.finishReparsedNode(heritageClause, implementsTag.ClassName)
|
|
|
|
if class.HeritageClauses == nil {
|
|
heritageClauses := p.newNodeList(implementsTag.ClassName.Loc, p.nodeSliceArena.NewSlice1(heritageClause))
|
|
class.HeritageClauses = heritageClauses
|
|
} else {
|
|
class.HeritageClauses.Nodes = append(class.HeritageClauses.Nodes, heritageClause)
|
|
}
|
|
p.finishMutatedNode(parent)
|
|
}
|
|
case ast.KindJSDocAugmentsTag:
|
|
if class := getClassLikeData(parent); class != nil && class.HeritageClauses != nil {
|
|
if extendsClause := core.Find(class.HeritageClauses.Nodes, func(node *ast.Node) bool {
|
|
return node.AsHeritageClause().Token == ast.KindExtendsKeyword
|
|
}); extendsClause != nil && len(extendsClause.AsHeritageClause().Types.Nodes) == 1 {
|
|
target := extendsClause.AsHeritageClause().Types.Nodes[0].AsExpressionWithTypeArguments()
|
|
source := tag.ClassName().AsExpressionWithTypeArguments()
|
|
if ast.HasSamePropertyAccessName(target.Expression, source.Expression) {
|
|
if target.TypeArguments == nil && source.TypeArguments != nil {
|
|
newArguments := p.nodeSliceArena.NewSlice(len(source.TypeArguments.Nodes))
|
|
for i, arg := range source.TypeArguments.Nodes {
|
|
newArguments[i] = p.addDeepCloneReparse(arg)
|
|
}
|
|
target.TypeArguments = p.newNodeList(source.TypeArguments.Loc, newArguments)
|
|
p.finishMutatedNode(target.AsNode())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Parser) makeQuestionIfOptional(parameter *ast.JSDocParameterOrPropertyTag) *ast.Node {
|
|
var questionToken *ast.Node
|
|
if parameter.IsBracketed || parameter.TypeExpression != nil && parameter.TypeExpression.Type().Kind == ast.KindJSDocOptionalType {
|
|
questionToken = p.factory.NewToken(ast.KindQuestionToken)
|
|
questionToken.Loc = parameter.Loc
|
|
questionToken.Flags = p.contextFlags | ast.NodeFlagsReparsed
|
|
}
|
|
return questionToken
|
|
}
|
|
|
|
func findMatchingParameter(fun *ast.Node, parameterTag *ast.JSDocParameterOrPropertyTag, jsDoc *ast.Node) (*ast.ParameterDeclaration, bool) {
|
|
tagIndex := -1
|
|
paramCount := -1
|
|
for _, tag := range jsDoc.AsJSDoc().Tags.Nodes {
|
|
if tag.Kind == ast.KindJSDocParameterTag {
|
|
paramCount++
|
|
if tag.AsJSDocParameterOrPropertyTag() == parameterTag {
|
|
tagIndex = paramCount
|
|
break
|
|
}
|
|
}
|
|
}
|
|
for parameterIndex, parameter := range fun.Parameters() {
|
|
if parameter.Name().Kind == ast.KindIdentifier {
|
|
if parameterTag.Name().Kind == ast.KindIdentifier &&
|
|
((parameter.Name().Text() == parameterTag.Name().Text()) || (parameterIndex == tagIndex && len(parameterTag.Name().Text()) == 0)) {
|
|
return parameter.AsParameterDeclaration(), true
|
|
}
|
|
} else if parameterIndex == tagIndex {
|
|
return parameter.AsParameterDeclaration(), true
|
|
}
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func skipSatisfiesExpressions(node *ast.Node) *ast.Node {
|
|
for node != nil && node.Kind == ast.KindSatisfiesExpression {
|
|
node = node.Expression()
|
|
}
|
|
return node
|
|
}
|
|
|
|
func getFunctionLikeHost(host *ast.Node) *ast.Node {
|
|
fun := host
|
|
switch host.Kind {
|
|
case ast.KindVariableStatement:
|
|
if nodes := host.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes; len(nodes) != 0 {
|
|
fun = nodes[0].Initializer()
|
|
}
|
|
case ast.KindPropertyAssignment, ast.KindPropertyDeclaration:
|
|
fun = host.Initializer()
|
|
case ast.KindExportAssignment, ast.KindReturnStatement:
|
|
fun = host.Expression()
|
|
case ast.KindExpressionStatement:
|
|
fun = ast.GetRightMostAssignedExpression(host.Expression())
|
|
}
|
|
fun = skipSatisfiesExpressions(fun)
|
|
if ast.IsFunctionLike(fun) {
|
|
return fun
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Parser) makeNewCast(t *ast.TypeNode, e *ast.Node, isAssertion bool) *ast.Node {
|
|
var assert *ast.Node
|
|
if isAssertion {
|
|
assert = p.factory.NewAsExpression(e, t)
|
|
} else {
|
|
assert = p.factory.NewSatisfiesExpression(e, t)
|
|
}
|
|
p.finishNodeWithEnd(assert, e.Pos(), e.End())
|
|
return assert
|
|
}
|
|
|
|
func getClassLikeData(parent *ast.Node) *ast.ClassLikeBase {
|
|
var class *ast.ClassLikeBase
|
|
switch parent.Kind {
|
|
case ast.KindClassDeclaration:
|
|
class = parent.AsClassDeclaration().ClassLikeData()
|
|
case ast.KindClassExpression:
|
|
class = parent.AsClassExpression().ClassLikeData()
|
|
}
|
|
return class
|
|
}
|
|
|
|
func (p *Parser) createExportModifier(locationNode *ast.Node) *ast.ModifierList {
|
|
exportModifier := p.factory.NewModifier(ast.KindExportKeyword)
|
|
exportModifier.Loc = locationNode.Loc
|
|
exportModifier.Flags = p.contextFlags | ast.NodeFlagsReparsed
|
|
nodes := p.nodeSliceArena.NewSlice1(exportModifier)
|
|
return p.newModifierList(locationNode.Loc, nodes)
|
|
}
|
|
|
|
// getInnermostNameOfJSDocNamespace returns the innermost identifier from a
|
|
// JSDoc namespace chain (ModuleDeclaration). For a simple identifier, it returns
|
|
// the identifier itself. For "A.B.C", it returns the identifier "C".
|
|
func (p *Parser) getInnermostNameOfJSDocNamespace(fullName *ast.Node) *ast.Node {
|
|
if fullName == nil {
|
|
return nil
|
|
}
|
|
for fullName.Kind == ast.KindModuleDeclaration {
|
|
body := fullName.AsModuleDeclaration().Body
|
|
if body == nil {
|
|
return fullName.Name()
|
|
}
|
|
fullName = body
|
|
}
|
|
return fullName
|
|
}
|
|
|
|
// wrapInJSDocNamespace wraps a statement (typically a type alias) in namespace
|
|
// declarations corresponding to a JSDoc dotted name. For example, given name
|
|
// "A.B.C" and a type alias for C, this produces:
|
|
//
|
|
// namespace A { namespace B { type C = ... } }
|
|
//
|
|
// If the name is a simple identifier (not a ModuleDeclaration), it returns the
|
|
// statement as-is.
|
|
func (p *Parser) wrapInJSDocNamespace(fullName *ast.Node, statement *ast.Node, nested bool) *ast.Node {
|
|
if fullName == nil || !ast.IsModuleDeclaration(fullName) {
|
|
return statement
|
|
}
|
|
// Recursively wrap from outermost to innermost. Inner namespaces always get an export modifier
|
|
// so members are accessible via dotted access from outside. The outermost namespace is treated as
|
|
// exported only in module files via IsImplicitlyExportedJSDocDeclaration (in the binder), so it
|
|
// does not get an explicit export modifier here.
|
|
wrapped := p.wrapInJSDocNamespace(fullName.Body(), statement, true /*nested*/)
|
|
block := p.factory.NewModuleBlock(p.newNodeList(fullName.Loc, p.nodeSliceArena.NewSlice1(wrapped)))
|
|
p.finishReparsedNode(block, fullName)
|
|
var modifiers *ast.ModifierList
|
|
if nested {
|
|
modifiers = p.createExportModifier(fullName)
|
|
}
|
|
result := p.factory.NewModuleDeclaration(modifiers, ast.KindNamespaceKeyword, p.addDeepCloneReparse(fullName.Name()), block)
|
|
p.finishReparsedNode(result, fullName)
|
|
p.reparsedClones = append(p.reparsedClones, result)
|
|
return result
|
|
}
|