Skip to content

Commit 0c1088c

Browse files
committed
preserve jsdoc base identity for call-expression heritage
1 parent 459b499 commit 0c1088c

5 files changed

Lines changed: 70 additions & 41 deletions

File tree

‎tsc/internal/ast/utilities.go‎

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,24 +1645,12 @@ func IsDottedName(node *Node) bool {
16451645
return false
16461646
}
16471647

1648-
func HasSamePropertyAccessName(target, source *Node) bool {
1649-
target = SkipParentheses(target)
1650-
source = SkipParentheses(source)
1651-
if IsCallExpression(target) {
1652-
target = SkipParentheses(target.Expression())
1653-
if IsPropertyAccessExpression(target) {
1654-
if HasSamePropertyAccessName(target, source) {
1655-
return true
1656-
}
1657-
target = SkipParentheses(target.Expression())
1658-
}
1659-
}
1660-
if target.Kind == KindIdentifier && source.Kind == KindIdentifier {
1661-
return target.Text() == source.Text()
1662-
}
1663-
if target.Kind == KindPropertyAccessExpression && source.Kind == KindPropertyAccessExpression {
1664-
return target.AsPropertyAccessExpression().Name().Text() == source.AsPropertyAccessExpression().Name().Text() &&
1665-
HasSamePropertyAccessName(target.Expression(), source.Expression())
1648+
func HasSamePropertyAccessName(node1, node2 *Node) bool {
1649+
if node1.Kind == KindIdentifier && node2.Kind == KindIdentifier {
1650+
return node1.Text() == node2.Text()
1651+
} else if node1.Kind == KindPropertyAccessExpression && node2.Kind == KindPropertyAccessExpression {
1652+
return node1.AsPropertyAccessExpression().Name().Text() == node2.AsPropertyAccessExpression().Name().Text() &&
1653+
HasSamePropertyAccessName(node1.Expression(), node2.Expression())
16661654
}
16671655
return false
16681656
}

‎tsc/internal/parser/parser_test.go‎

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -318,25 +318,32 @@ class A {
318318
}
319319
320320
/** @extends {A<string>} */
321-
class B extends A.extend() {}
322-
323-
/** @extends {Other<string>} */
324-
class C extends A.extend() {}`
321+
class B extends A.extend() {}`
325322
opts := ast.SourceFileParseOptions{
326323
FileName: "/index.js",
327324
Path: "/index.js",
328325
}
329326

330327
file := parser.ParseSourceFile(opts, sourceText, core.ScriptKindJS)
331328
statements := file.Statements.Nodes
332-
assert.Equal(t, len(statements), 3)
329+
assert.Equal(t, len(statements), 2)
333330

334331
classB := statements[1]
335332
assert.Assert(t, ast.IsClassDeclaration(classB))
336333

337334
baseType := ast.GetClassExtendsHeritageElement(classB)
338335
assert.Assert(t, baseType != nil)
339-
assert.Assert(t, ast.IsCallExpression(baseType.Expression()))
336+
337+
baseExpression := baseType.Expression()
338+
assert.Assert(t, ast.IsAsExpression(baseExpression))
339+
340+
callExpression := baseExpression.Expression()
341+
assert.Assert(t, ast.IsCallExpression(callExpression))
342+
assert.Equal(t, scanner.GetTextOfNode(callExpression), "A.extend()")
343+
344+
typeQuery := baseExpression.Type()
345+
assert.Equal(t, typeQuery.Kind, ast.KindTypeQuery)
346+
assert.Assert(t, typeQuery.Flags&ast.NodeFlagsReparsed != 0)
340347

341348
typeArguments := baseType.TypeArguments()
342349
assert.Equal(t, len(typeArguments), 1)
@@ -354,18 +361,11 @@ class C extends A.extend() {}`
354361

355362
tag := tags.Nodes[0]
356363
assert.Assert(t, ast.IsJSDocAugmentsTag(tag))
364+
assert.Equal(t, ast.GetReparsedNodeForNode(tag.ClassName().Expression()), typeQuery.AsTypeQueryNode().ExprName)
357365

358366
sourceTypeArguments := tag.ClassName().TypeArguments()
359367
assert.Equal(t, len(sourceTypeArguments), 1)
360368
assert.Equal(t, ast.GetReparsedNodeForNode(sourceTypeArguments[0]), typeArgument)
361-
362-
classC := statements[2]
363-
assert.Assert(t, ast.IsClassDeclaration(classC))
364-
365-
mismatchedBaseType := ast.GetClassExtendsHeritageElement(classC)
366-
assert.Assert(t, mismatchedBaseType != nil)
367-
assert.Assert(t, ast.IsCallExpression(mismatchedBaseType.Expression()))
368-
assert.Equal(t, len(mismatchedBaseType.TypeArguments()), 0)
369369
}
370370

371371
func TestSourceFilePositionMapWithNonASCIIStringLiteral(t *testing.T) {

‎tsc/internal/parser/reparser.go‎

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -597,21 +597,30 @@ func (p *Parser) reparseHosted(tag *ast.Node, parent *ast.Node, jsDoc *ast.Node)
597597
}); extendsClause != nil && len(extendsClause.AsHeritageClause().Types.Nodes) == 1 {
598598
target := extendsClause.AsHeritageClause().Types.Nodes[0].AsExpressionWithTypeArguments()
599599
source := tag.ClassName().AsExpressionWithTypeArguments()
600-
if ast.HasSamePropertyAccessName(target.Expression, source.Expression) {
601-
if target.TypeArguments == nil && source.TypeArguments != nil {
602-
newArguments := p.nodeSliceArena.NewSlice(len(source.TypeArguments.Nodes))
603-
for i, arg := range source.TypeArguments.Nodes {
604-
newArguments[i] = p.addDeepCloneReparse(arg)
605-
}
606-
target.TypeArguments = p.newNodeList(source.TypeArguments.Loc, newArguments)
607-
p.finishMutatedNode(target.AsNode())
600+
if target.TypeArguments == nil && source.TypeArguments != nil {
601+
if ast.IsCallExpression(ast.SkipParentheses(target.Expression)) {
602+
typeQuery := p.factory.NewTypeQueryNode(p.addDeepCloneReparse(source.Expression), nil)
603+
p.finishReparsedNode(typeQuery, source.Expression)
604+
target.Expression = p.makeNewCast(typeQuery, target.Expression, true /*isAssertion*/)
605+
p.setReparsedTypeArguments(target, source.TypeArguments)
606+
} else if ast.HasSamePropertyAccessName(target.Expression, source.Expression) {
607+
p.setReparsedTypeArguments(target, source.TypeArguments)
608608
}
609609
}
610610
}
611611
}
612612
}
613613
}
614614

615+
func (p *Parser) setReparsedTypeArguments(target *ast.ExpressionWithTypeArguments, source *ast.NodeList) {
616+
typeArguments := p.nodeSliceArena.NewSlice(len(source.Nodes))
617+
for i, typeArgument := range source.Nodes {
618+
typeArguments[i] = p.addDeepCloneReparse(typeArgument)
619+
}
620+
target.TypeArguments = p.newNodeList(source.Loc, typeArguments)
621+
p.finishMutatedNode(target.AsNode())
622+
}
623+
615624
func (p *Parser) makeQuestionIfOptional(parameter *ast.JSDocParameterOrPropertyTag) *ast.Node {
616625
var questionToken *ast.Node
617626
if parameter.IsBracketed || parameter.TypeExpression != nil && parameter.TypeExpression.Type().Kind == ast.KindJSDocOptionalType {

‎tsc/testdata/tests/cases/conformance/jsdoc/extendsTag7.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @target: es2015
22
// @allowJs: true
33
// @checkJs: true
4-
// @noEmit: true
4+
// @declaration: true
55

66
// @filename: a.js
77
/** @template T */
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// @target: es2015
2+
// @allowJs: true
3+
// @checkJs: true
4+
// @noEmit: true
5+
6+
// @filename: a.js
7+
/** @template T */
8+
class A {
9+
/** @returns {T} */
10+
get a() {
11+
throw new Error();
12+
}
13+
14+
/** @returns {typeof C} */
15+
static extend() {
16+
return C;
17+
}
18+
}
19+
20+
/** @template T */
21+
class C {
22+
/** @returns {T} */
23+
get c() {
24+
throw new Error();
25+
}
26+
}
27+
28+
/** @extends {A<string>} */
29+
class B extends A.extend() {}
30+
31+
const a = new B().a;
32+
const c = new B().c;

0 commit comments

Comments
 (0)