Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -9930,6 +9930,7 @@ type SourceFile struct {
AmbientModuleNames []string
CommentDirectives []CommentDirective
jsdocCache map[*Node][]*Node
ReparsedNodes map[*Node]*Node
Pragmas []Pragma
ReferencedFiles []*FileReference
TypeReferenceDirectives []*FileReference
Expand Down
6 changes: 5 additions & 1 deletion internal/binder/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -581,7 +581,11 @@ func (b *Binder) bind(node *ast.Node) bool {
if node == nil {
return false
}
node.Parent = b.parent
if node.Parent == nil {
node.Parent = b.parent
} else if node.Parent != b.parent {
panic("parent pointer already set, but doesn't match - collectModuleReferences probably out of sync with binder")
}
saveInStrictMode := b.inStrictMode
// Even though in the AST the jsdoc @typedef node belongs to the current node,
// its symbol might be in the same scope with the current node's symbol. Consider:
Expand Down
6 changes: 0 additions & 6 deletions internal/parser/jsdoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,6 @@ func (p *Parser) parseJSDocTypeExpression(mayOmitBraces bool) *ast.Node {
}

result := p.factory.NewJSDocTypeExpression(t)
// normally parent references are set during binding. However, for clients that only need
// a syntax tree, and no semantic features, then the binding process is an unnecessary
// overhead. This functions allows us to set all the parents, without all the expense of
// binding.
ast.SetParentInChildren(result)
p.finishNode(result, pos)
return result
}
Expand All @@ -105,7 +100,6 @@ func (p *Parser) parseJSDocNameReference() *ast.Node {
}

result := p.factory.NewJSDocNameReference(entityName)
ast.SetParentInChildren(result)
p.finishNode(result, pos)
return result
}
Expand Down
2 changes: 2 additions & 0 deletions internal/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type Parser struct {
jsdocCommentRangesSpace []ast.CommentRange
jsdocTagCommentsSpace []string
reparseList []*ast.Node
reparsedNodes map[*ast.Node]*ast.Node
commonJSModuleIndicator *ast.Node
}

Expand Down Expand Up @@ -354,6 +355,7 @@ func (p *Parser) finishSourceFile(result *ast.SourceFile, isDeclarationFile bool
result.NodeCount = p.factory.NodeCount()
result.TextCount = p.factory.TextCount()
result.IdentifierCount = p.identifierCount
result.ReparsedNodes = p.reparsedNodes
result.SetJSDocCache(p.jsdocCache)
}

Expand Down
53 changes: 38 additions & 15 deletions internal/parser/reparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
export.Flags = ast.NodeFlagsReparsed
export.Loc = bin.Loc
p.reparseList = append(p.reparseList, export)
p.setReparsed(node, export)
p.commonJSModuleIndicator = export
}
}
Expand Down Expand Up @@ -63,7 +64,7 @@
var t *ast.Node
switch typeExpression.Kind {
case ast.KindJSDocTypeExpression:
t = typeExpression.Type()
t = p.factory.DeepCloneNode(typeExpression.Type())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you need to clone every child added to a synthetic node? There are a few up in reparseCommonJS too (eg bin.Left)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, good catch.

case ast.KindJSDocTypeLiteral:
members := p.nodeSlicePool.NewSlice(0)
for _, member := range typeExpression.AsJSDocTypeLiteral().JSDocPropertyTags {
Expand All @@ -74,7 +75,7 @@
questionToken.Loc = core.NewTextRange(member.Pos(), member.End())
questionToken.Flags = p.contextFlags | ast.NodeFlagsReparsed
}
prop := p.factory.NewPropertySignatureDeclaration(nil, member.Name(), questionToken, member.Type(), nil /*initializer*/)
prop := p.factory.NewPropertySignatureDeclaration(nil, p.factory.DeepCloneNode(member.Name()), questionToken, p.factory.DeepCloneNode(member.Type()), nil /*initializer*/)
prop.Loc = member.Loc
prop.Flags = p.contextFlags | ast.NodeFlagsReparsed
members = append(members, prop)
Expand All @@ -85,19 +86,30 @@
default:
panic("typedef tag type expression should be a name reference or a type expression" + typeExpression.Kind.String())
}
typeAlias := p.factory.NewJSTypeAliasDeclaration(modifiers, tag.AsJSDocTypedefTag().Name(), typeParameters, t)
typeAlias := p.factory.NewJSTypeAliasDeclaration(modifiers, p.factory.DeepCloneNode(tag.AsJSDocTypedefTag().Name()), typeParameters, t)
typeAlias.Loc = core.NewTextRange(tag.Pos(), tag.End())
typeAlias.Flags = p.contextFlags | ast.NodeFlagsReparsed
ast.SetParentInChildren(typeAlias)
p.reparseList = append(p.reparseList, typeAlias)
p.setReparsed(tag, typeAlias)
case ast.KindJSDocImportTag:
importTag := tag.AsJSDocImportTag()
importClause := importTag.ImportClause.Clone(&p.factory)
importClause := p.factory.DeepCloneNode(importTag.ImportClause)
importClause.Flags |= ast.NodeFlagsReparsed
var modifiers *ast.ModifierList
if importTag.Modifiers() != nil {
modifiers := importTag.Modifiers().Clone(&p.factory)

Check failure on line 101 in internal/parser/reparser.go

View workflow job for this annotation

GitHub Actions / lint (ubuntu-latest)

shadow: declaration of "modifiers" shadows declaration at line 99 and is reachable from use at line 108 (customlint)
for i, m := range modifiers.Nodes {
modifiers.Nodes[i] = p.factory.DeepCloneNode(m)
modifiers.Nodes[i].Flags |= ast.NodeFlagsReparsed
}
}
importClause.AsImportClause().IsTypeOnly = true
importDeclaration := p.factory.NewJSImportDeclaration(importTag.Modifiers(), importClause, importTag.ModuleSpecifier, importTag.Attributes)
importDeclaration := p.factory.NewJSImportDeclaration(modifiers, importClause, p.factory.DeepCloneNode(importTag.ModuleSpecifier), p.factory.DeepCloneNode(importTag.Attributes))
importDeclaration.Loc = core.NewTextRange(tag.Pos(), tag.End())
importDeclaration.Flags = p.contextFlags | ast.NodeFlagsReparsed
p.reparseList = append(p.reparseList, importDeclaration)
p.setReparsed(tag, importDeclaration)
// !!! @overload and other unattached tags (@callback et al) support goes here
}
if !isLast {
Expand Down Expand Up @@ -209,16 +221,15 @@
constraint := tag.AsJSDocTemplateTag().Constraint
for _, tp := range tag.AsJSDocTemplateTag().TypeParameters().Nodes {
typeParameter := tp.AsTypeParameter()
var reparse *ast.Node
if constraint == nil {
reparse = typeParameter.Clone(&p.factory)
} else {
clone := constraint.Type().Clone(&p.factory)
clone.Flags |= ast.NodeFlagsReparsed
reparse = p.factory.NewTypeParameterDeclaration(typeParameter.Modifiers(), typeParameter.Name(), clone, typeParameter.DefaultType)
reparse.Loc = typeParameter.Loc
reparse := p.factory.DeepCloneNode(tp)
reparse.Loc = typeParameter.Loc
if constraint != nil {
constraintClone := p.factory.DeepCloneNode(constraint.Type())
constraintClone.Flags |= ast.NodeFlagsReparsed
reparse.AsTypeParameter().Constraint = constraintClone
}
reparse.Flags |= ast.NodeFlagsReparsed
p.setReparsed(tp, reparse)
typeParameters = append(typeParameters, reparse)
}
}
Expand Down Expand Up @@ -255,9 +266,13 @@
}

func (p *Parser) makeNewTypeAssertion(t *ast.TypeNode, e *ast.Node) *ast.Node {
assert := p.factory.NewTypeAssertion(t, e)
if t.Flags&ast.NodeFlagsReparsed == 0 {
panic("should only pass reparsed type nodes to makeNewTypeAssertion")
}
assert := p.factory.NewTypeAssertion(t, p.factory.DeepCloneNode(e))
assert.Flags = p.contextFlags | ast.NodeFlagsReparsed
assert.Loc = core.NewTextRange(e.Pos(), e.End())
p.setReparsed(e, assert)
return assert
}

Expand All @@ -270,10 +285,18 @@
} else {
panic("JSDoc type expression already has a host: " + typeExpression.AsJSDocTypeExpression().Host.Kind.String())
}
t := typeExpression.Type().Clone(&p.factory)
t := p.factory.DeepCloneNode(typeExpression.Type())
t.Flags |= ast.NodeFlagsReparsed
if host != nil {
t.Parent = host
}
p.setReparsed(typeExpression.Type(), t)
return t
}

func (p *Parser) setReparsed(original *ast.Node, reparsed *ast.Node) {
if p.reparsedNodes == nil {
p.reparsedNodes = make(map[*ast.Node]*ast.Node)
}
p.reparsedNodes[original] = reparsed
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ export = Foo;
/** @typedef {(foo: Foo) => string} FooFun */

module.exports = /** @type {FooFun} */(void 0);
>module.exports = /** @type {FooFun} */(void 0) : (foo: typeof Foo) => string
>module.exports : (foo: typeof Foo) => string
>module : { export=: (foo: typeof Foo) => string; }
>exports : (foo: typeof Foo) => string
>(void 0) : (foo: typeof Foo) => string
>void 0 : (foo: typeof Foo) => string
>module.exports = /** @type {FooFun} */(void 0) : FooFun
>module.exports : FooFun
>module : { export=: FooFun; }
>exports : FooFun
>(void 0) : FooFun
>void 0 : FooFun
>void 0 : undefined
>0 : 0

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function acceptNum(num) {}

/** @type {Fn} */
const fn1 =
>fn1 : (a: string, b: number) => void
>fn1 : Fn

/**
* @param [b]
Expand Down Expand Up @@ -46,7 +46,7 @@ const fn1 =

/** @type {Fn} */
const fn2 =
>fn2 : (a: string, b: number) => void
>fn2 : Fn

/**
* @param {number} [b]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
input.js(5,48): error TS2304: Cannot find name 'P'.
input.js(48,1): error TS2309: An export assignment cannot be used in a module with other exported elements.


==== input.js (2 errors) ====
==== input.js (1 errors) ====
/** @typedef {{ color: "red" | "blue" }} MyComponentProps */

/**
* @template P
* @typedef {{ (): any; defaultProps?: Partial<P> }} StatelessComponent */
~
!!! error TS2304: Cannot find name 'P'.

/**
* @type {StatelessComponent<MyComponentProps>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,36 @@
* @type {StatelessComponent<MyComponentProps>}
*/
const MyComponent = () => /* @type {any} */(null);
>MyComponent : { (): any; defaultProps?: P; }
>() => /* @type {any} */(null) : { (): any; defaultProps: { color: string; }; }
>MyComponent : StatelessComponent<MyComponentProps>
>() => /* @type {any} */(null) : { (): any; defaultProps: { color: "red"; }; }
>(null) : null

MyComponent.defaultProps = {
>MyComponent.defaultProps = { color: "red"} : { color: string; }
>MyComponent.defaultProps : P
>MyComponent : { (): any; defaultProps?: P; }
>defaultProps : P
>{ color: "red"} : { color: string; }
>MyComponent.defaultProps = { color: "red"} : { color: "red"; }
>MyComponent.defaultProps : Partial<MyComponentProps>
>MyComponent : StatelessComponent<MyComponentProps>
>defaultProps : Partial<MyComponentProps>
>{ color: "red"} : { color: "red"; }

color: "red"
>color : string
>color : "red"
>"red" : "red"

};

const MyComponent2 = () => null;
>MyComponent2 : { (): any; defaultProps: { color: "blue" | "red"; }; }
>() => null : { (): any; defaultProps: { color: "blue" | "red"; }; }
>MyComponent2 : { (): any; defaultProps: MyComponentProps; }
>() => null : { (): any; defaultProps: MyComponentProps; }

/**
* @type {MyComponentProps}
*/
MyComponent2.defaultProps = {
>MyComponent2.defaultProps = { color: "red"} : { color: "blue" | "red"; }
>MyComponent2.defaultProps : { color: "blue" | "red"; }
>MyComponent2 : { (): any; defaultProps: { color: "blue" | "red"; }; }
>defaultProps : { color: "blue" | "red"; }
>{ color: "red"} : { color: "blue" | "red"; }
>MyComponent2.defaultProps = { color: "red"} : MyComponentProps
>MyComponent2.defaultProps : MyComponentProps
>MyComponent2 : { (): any; defaultProps: MyComponentProps; }
>defaultProps : MyComponentProps
>{ color: "red"} : MyComponentProps
>{ color: "red"} : { color: "red"; }

color: "red"
Expand All @@ -52,16 +52,16 @@ MyComponent2.defaultProps = {
* @type {StatelessComponent<MyComponentProps>}
*/
const check = MyComponent2;
>check : { (): any; defaultProps?: P; }
>MyComponent2 : { (): any; defaultProps: { color: "blue" | "red"; }; }
>check : StatelessComponent<MyComponentProps>
>MyComponent2 : { (): any; defaultProps: MyComponentProps; }

/**
*
* @param {{ props: MyComponentProps }} p
*/
function expectLiteral(p) {}
>expectLiteral : (p: { props: { color: "blue" | "red"; }; }) => void
>p : { props: { color: "blue" | "red"; }; }
>expectLiteral : (p: { props: MyComponentProps; }) => void
>p : { props: MyComponentProps; }

function foo() {
>foo : () => void
Expand All @@ -70,30 +70,30 @@ function foo() {
* @type {MyComponentProps}
*/
this.props = { color: "red" };
>this.props = { color: "red" } : { color: "blue" | "red"; }
>this.props = { color: "red" } : MyComponentProps
>this.props : any
>this : any
>props : any
>{ color: "red" } : { color: "blue" | "red"; }
>{ color: "red" } : MyComponentProps
>{ color: "red" } : { color: "red"; }
>color : "red"
>"red" : "red"

expectLiteral(this);
>expectLiteral(this) : void
>expectLiteral : (p: { props: { color: "blue" | "red"; }; }) => void
>expectLiteral : (p: { props: MyComponentProps; }) => void
>this : any
}

/**
* @type {MyComponentProps}
*/
module.exports = {
>module.exports = { color: "red"} : { color: "blue" | "red"; }
>module.exports : { color: "blue" | "red"; }
>module : { export=: { color: "blue" | "red"; }; }
>exports : { color: "blue" | "red"; }
>{ color: "red"} : { color: "blue" | "red"; }
>module.exports = { color: "red"} : MyComponentProps
>module.exports : MyComponentProps
>module : { export=: MyComponentProps; }
>exports : MyComponentProps
>{ color: "red"} : MyComponentProps
>{ color: "red"} : { color: "red"; }

color: "red"
Expand All @@ -103,10 +103,10 @@ module.exports = {

expectLiteral({ props: module.exports });
>expectLiteral({ props: module.exports }) : void
>expectLiteral : (p: { props: { color: "blue" | "red"; }; }) => void
>{ props: module.exports } : { props: { color: "blue" | "red"; }; }
>props : { color: "blue" | "red"; }
>module.exports : { color: "blue" | "red"; }
>module : { export=: { color: "blue" | "red"; }; }
>exports : { color: "blue" | "red"; }
>expectLiteral : (p: { props: MyComponentProps; }) => void
>{ props: module.exports } : { props: MyComponentProps; }
>props : MyComponentProps
>module.exports : MyComponentProps
>module : { export=: MyComponentProps; }
>exports : MyComponentProps

Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
*/

/** @type {NumberLike[]} */export default ([ ]);
>([ ]) : (string | number)[]
>([ ]) : NumberLike[]
>([ ]) : undefined[]
>[ ] : undefined[]

=== b.ts ===
import A from './a'
>A : (string | number)[]
>A : NumberLike[]

A[0]
>A[0] : string | number
>A : (string | number)[]
>A[0] : NumberLike
>A : NumberLike[]
>0 : 0

Loading
Loading