Skip to content

Add the 5 modifier jsdoc tags #927

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 11, 2025
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
16 changes: 13 additions & 3 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ func (n *Node) TemplateLiteralLikeData() *TemplateLiteralLikeBase {
return n.data.TemplateLiteralLikeData()
}

type mutableNode Node

func (n *Node) AsMutable() *mutableNode { return (*mutableNode)(n) }
func (n *mutableNode) SetModifiers(modifiers *ModifierList) { n.data.setModifiers(modifiers) }

func (n *Node) Symbol() *Symbol {
data := n.DeclarationData()
if data != nil {
Expand Down Expand Up @@ -1644,6 +1649,7 @@ type nodeData interface {
Clone(v NodeFactoryCoercible) *Node
Name() *DeclarationName
Modifiers() *ModifierList
setModifiers(modifiers *ModifierList)
FlowNodeData() *FlowNodeBase
DeclarationData() *DeclarationBase
ExportableData() *ExportableBase
Expand Down Expand Up @@ -1671,6 +1677,7 @@ func (node *NodeDefault) VisitEachChild(v *NodeVisitor) *Node { re
func (node *NodeDefault) Clone(v NodeFactoryCoercible) *Node { return nil }
func (node *NodeDefault) Name() *DeclarationName { return nil }
func (node *NodeDefault) Modifiers() *ModifierList { return nil }
func (node *NodeDefault) setModifiers(modifiers *ModifierList) {}
func (node *NodeDefault) FlowNodeData() *FlowNodeBase { return nil }
func (node *NodeDefault) DeclarationData() *DeclarationBase { return nil }
func (node *NodeDefault) ExportableData() *ExportableBase { return nil }
Expand Down Expand Up @@ -4802,9 +4809,10 @@ type NamedMemberBase struct {
PostfixToken *TokenNode // TokenNode. Optional
}

func (node *NamedMemberBase) DeclarationData() *DeclarationBase { return &node.DeclarationBase }
func (node *NamedMemberBase) Modifiers() *ModifierList { return node.modifiers }
func (node *NamedMemberBase) Name() *DeclarationName { return node.name }
func (node *NamedMemberBase) DeclarationData() *DeclarationBase { return &node.DeclarationBase }
func (node *NamedMemberBase) Modifiers() *ModifierList { return node.modifiers }
func (node *NamedMemberBase) setModifiers(modifiers *ModifierList) { node.modifiers = modifiers }
func (node *NamedMemberBase) Name() *DeclarationName { return node.name }

// CallSignatureDeclaration

Expand Down Expand Up @@ -5612,6 +5620,8 @@ func (node *BinaryExpression) computeSubtreeFacts() SubtreeFacts {
core.IfElse(node.OperatorToken.Kind == KindInKeyword && IsPrivateIdentifier(node.Left), SubtreeContainsClassFields, SubtreeFactsNone)
}

func (node *BinaryExpression) setModifiers(modifiers *ModifierList) { node.modifiers = modifiers }

func IsBinaryExpression(node *Node) bool {
return node.Kind == KindBinaryExpression
}
Expand Down
38 changes: 19 additions & 19 deletions internal/checker/grammarchecks.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,11 @@ func (c *Checker) checkGrammarModifiers(node *ast.Node /*Union[HasModifiers, Has
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_already_seen, "override")
} else if flags&ast.ModifierFlagsAmbient != 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "override", "declare")
} else if flags&ast.ModifierFlagsReadonly != 0 {
} else if flags&ast.ModifierFlagsReadonly != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "override", "readonly")
} else if flags&ast.ModifierFlagsAccessor != 0 {
} else if flags&ast.ModifierFlagsAccessor != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "override", "accessor")
} else if flags&ast.ModifierFlagsAsync != 0 {
} else if flags&ast.ModifierFlagsAsync != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "override", "async")
}
flags |= ast.ModifierFlagsOverride
Expand All @@ -325,22 +325,22 @@ func (c *Checker) checkGrammarModifiers(node *ast.Node /*Union[HasModifiers, Has

if flags&ast.ModifierFlagsAccessibilityModifier != 0 {
return c.grammarErrorOnNode(modifier, diagnostics.Accessibility_modifier_already_seen)
} else if flags&ast.ModifierFlagsOverride != 0 {
} else if flags&ast.ModifierFlagsOverride != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, text, "override")
} else if flags&ast.ModifierFlagsStatic != 0 {
} else if flags&ast.ModifierFlagsStatic != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, text, "static")
} else if flags&ast.ModifierFlagsAccessor != 0 {
} else if flags&ast.ModifierFlagsAccessor != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, text, "accessor")
} else if flags&ast.ModifierFlagsReadonly != 0 {
} else if flags&ast.ModifierFlagsReadonly != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, text, "readonly")
} else if flags&ast.ModifierFlagsAsync != 0 {
} else if flags&ast.ModifierFlagsAsync != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, text, "async")
} else if node.Parent.Kind == ast.KindModuleBlock || node.Parent.Kind == ast.KindSourceFile {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_module_or_namespace_element, text)
} else if flags&ast.ModifierFlagsAbstract != 0 {
if modifier.Kind == ast.KindPrivateKeyword {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, text, "abstract")
} else {
} else if modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, text, "abstract")
}
} else if ast.IsPrivateIdentifierClassElementDeclaration(node) {
Expand All @@ -350,19 +350,19 @@ func (c *Checker) checkGrammarModifiers(node *ast.Node /*Union[HasModifiers, Has
case ast.KindStaticKeyword:
if flags&ast.ModifierFlagsStatic != 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_already_seen, "static")
} else if flags&ast.ModifierFlagsReadonly != 0 {
} else if flags&ast.ModifierFlagsReadonly != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "static", "readonly")
} else if flags&ast.ModifierFlagsAsync != 0 {
} else if flags&ast.ModifierFlagsAsync != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "static", "async")
} else if flags&ast.ModifierFlagsAccessor != 0 {
} else if flags&ast.ModifierFlagsAccessor != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "static", "accessor")
} else if node.Parent.Kind == ast.KindModuleBlock || node.Parent.Kind == ast.KindSourceFile {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_module_or_namespace_element, "static")
} else if node.Kind == ast.KindParameter {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_parameter, "static")
} else if flags&ast.ModifierFlagsAbstract != 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "static", "abstract")
} else if flags&ast.ModifierFlagsOverride != 0 {
} else if flags&ast.ModifierFlagsOverride != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "static", "override")
}
flags |= ast.ModifierFlagsStatic
Expand Down Expand Up @@ -395,11 +395,11 @@ func (c *Checker) checkGrammarModifiers(node *ast.Node /*Union[HasModifiers, Has
}
if flags&ast.ModifierFlagsExport != 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_already_seen, "export")
} else if flags&ast.ModifierFlagsAmbient != 0 {
} else if flags&ast.ModifierFlagsAmbient != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "export", "declare")
} else if flags&ast.ModifierFlagsAbstract != 0 {
} else if flags&ast.ModifierFlagsAbstract != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "export", "abstract")
} else if flags&ast.ModifierFlagsAsync != 0 {
} else if flags&ast.ModifierFlagsAsync != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "export", "async")
} else if ast.IsClassLike(node.Parent) && !ast.IsJSTypeAliasDeclaration(node) {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_class_elements_of_this_kind, "export")
Expand All @@ -424,7 +424,7 @@ func (c *Checker) checkGrammarModifiers(node *ast.Node /*Union[HasModifiers, Has
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_a_using_declaration, "default")
} else if blockScopeKind == ast.NodeFlagsAwaitUsing {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_cannot_appear_on_an_await_using_declaration, "default")
} else if flags&ast.ModifierFlagsExport == 0 {
} else if flags&ast.ModifierFlagsExport == 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "export", "default")
} else if sawExportBeforeDecorators {
return c.grammarErrorOnNode(firstDecorator, diagnostics.Decorators_are_not_valid_here)
Expand Down Expand Up @@ -481,10 +481,10 @@ func (c *Checker) checkGrammarModifiers(node *ast.Node /*Union[HasModifiers, Has
if flags&ast.ModifierFlagsAsync != 0 && lastAsync != nil {
return c.grammarErrorOnNode(lastAsync, diagnostics.X_0_modifier_cannot_be_used_with_1_modifier, "async", "abstract")
}
if flags&ast.ModifierFlagsOverride != 0 {
if flags&ast.ModifierFlagsOverride != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "abstract", "override")
}
if flags&ast.ModifierFlagsAccessor != 0 {
if flags&ast.ModifierFlagsAccessor != 0 && modifier.Flags&ast.NodeFlagsReparsed == 0 {
return c.grammarErrorOnNode(modifier, diagnostics.X_0_modifier_must_precede_1_modifier, "abstract", "accessor")
}
}
Expand Down
74 changes: 56 additions & 18 deletions internal/parser/reparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,53 +183,57 @@ func (p *Parser) gatherTypeParameters(j *ast.Node) *ast.NodeList {
func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node) {
switch tag.Kind {
case ast.KindJSDocTypeTag:
if parent.Kind == ast.KindVariableStatement && parent.AsVariableStatement().DeclarationList != nil {
for _, declaration := range parent.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
if declaration.AsVariableDeclaration().Type == nil {
declaration.AsVariableDeclaration().Type = p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, declaration)
break
switch parent.Kind {
case ast.KindVariableStatement:
if parent.AsVariableStatement().DeclarationList != nil {
for _, declaration := range parent.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
if declaration.AsVariableDeclaration().Type == nil {
declaration.AsVariableDeclaration().Type = p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, declaration)
break
}
}
}
} else if parent.Kind == ast.KindVariableDeclaration {
case ast.KindVariableDeclaration:
if parent.AsVariableDeclaration().Type == nil {
parent.AsVariableDeclaration().Type = p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, parent)
}
} else if parent.Kind == ast.KindCommonJSExport {
case ast.KindCommonJSExport:
export := parent.AsCommonJSExport()
if export.Type == nil {
export.Type = p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, parent)
}
} else if parent.Kind == ast.KindPropertyDeclaration {
case ast.KindPropertyDeclaration:
declaration := parent.AsPropertyDeclaration()
if declaration.Type == nil {
declaration.Type = p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, parent)
}
} else if parent.Kind == ast.KindPropertyAssignment {
case ast.KindPropertyAssignment:
prop := parent.AsPropertyAssignment()
if prop.Type == nil {
prop.Type = p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, parent)
}
} else if parent.Kind == ast.KindShorthandPropertyAssignment {
case ast.KindShorthandPropertyAssignment:
prop := parent.AsShorthandPropertyAssignment()
if prop.Type == nil {
prop.Type = p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, parent)
}
} else if parent.Kind == ast.KindExportAssignment || parent.Kind == ast.KindJSExportAssignment {
case ast.KindExportAssignment, ast.KindJSExportAssignment:
export := parent.AsExportAssignment()
if export.Type == nil {
export.Type = p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, parent)
}
} else if parent.Kind == ast.KindReturnStatement {
case ast.KindReturnStatement:
ret := parent.AsReturnStatement()
ret.Expression = p.makeNewTypeAssertion(p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, nil), ret.Expression)
} else if parent.Kind == ast.KindParenthesizedExpression {
case ast.KindParenthesizedExpression:
paren := parent.AsParenthesizedExpression()
paren.Expression = p.makeNewTypeAssertion(p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, nil), paren.Expression)
} else if parent.Kind == ast.KindExpressionStatement &&
parent.AsExpressionStatement().Expression.Kind == ast.KindBinaryExpression {
bin := parent.AsExpressionStatement().Expression.AsBinaryExpression()
if kind := ast.GetAssignmentDeclarationKind(bin); kind != ast.JSDeclarationKindNone {
bin.Type = p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, parent.AsExpressionStatement().Expression)
case ast.KindExpressionStatement:
if parent.AsExpressionStatement().Expression.Kind == ast.KindBinaryExpression {
bin := parent.AsExpressionStatement().Expression.AsBinaryExpression()
if kind := ast.GetAssignmentDeclarationKind(bin); kind != ast.JSDeclarationKindNone {
bin.Type = p.makeNewType(tag.AsJSDocTypeTag().TypeExpression, parent.AsExpressionStatement().Expression)
}
}
}
case ast.KindJSDocTemplateTag:
Expand Down Expand Up @@ -268,6 +272,40 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node)
fun.FunctionLikeData().Type = p.makeNewType(tag.AsJSDocReturnTag().TypeExpression, fun)
}
}
case ast.KindJSDocReadonlyTag, ast.KindJSDocPrivateTag, ast.KindJSDocPublicTag, ast.KindJSDocProtectedTag, ast.KindJSDocOverrideTag:
if parent.Kind == ast.KindExpressionStatement {
parent = parent.AsExpressionStatement().Expression
}
switch parent.Kind {
case ast.KindPropertyDeclaration, ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, 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.nodeSlicePool.NewSlice(1)
nodes[0] = modifier
loc = tag.Loc
} else {
nodes = append(parent.Modifiers().Nodes, modifier)
loc = parent.Modifiers().Loc
}
parent.AsMutable().SetModifiers(p.newModifierList(loc, nodes))
}
case ast.KindJSDocImplementsTag:
if class := getClassLikeData(parent); class != nil {
implementsTag := tag.AsJSDocImplementsTag()
Expand Down
Loading