Skip to content

Commit 334e0b6

Browse files
frigus02copybara-github
authored andcommitted
Fix go/ts52upgrade JSC_MISPLACED_ANNOTATION issue
For decorated classes, that reference the class name in a static block, TypeScript 5.2 changed the JS emit from /** @abstract */ let Foo = class Foo { to var Foo_1; /** @abstract */ let Foo = Foo_1 = class Foo { This caused JSC_MISPLACED_ANNOTATION errors for both `@abstract` and `@template`. The only fix needed is in the TypedScopeCreator. Optimizations InlineAndCollapseProperties and RemoveUnusedCode already don't optimize the 5.2 emit. They work the same on 5.3 emit. PiperOrigin-RevId: 561277685
1 parent 517545f commit 334e0b6

File tree

6 files changed

+93
-7
lines changed

6 files changed

+93
-7
lines changed

src/com/google/javascript/jscomp/CheckJSDoc.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ private boolean isClassDecl(Node n) {
308308
}
309309

310310
private boolean isNameInitializeWithClass(Node n) {
311-
return n != null && n.isName() && n.hasChildren() && isClass(n.getFirstChild());
311+
return n != null && n.isName() && n.hasChildren() && isClassDecl(n.getFirstChild());
312312
}
313313

314314
private boolean isClass(Node n) {

src/com/google/javascript/jscomp/TypedScopeCreator.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,6 +2397,14 @@ && shouldUseFunctionLiteralType(
23972397
}
23982398
}
23992399

2400+
if (rValue != null && rValue.isAssign()) {
2401+
// Handle nested assignments. For example, TypeScript generates code like this:
2402+
// var Foo_1;
2403+
// let Foo = Foo_1 = class Foo {}
2404+
// Foo = Foo_1 = tslib_1.decorate(..., Foo);
2405+
return getDeclaredType(info, lValue, rValue.getSecondChild(), null);
2406+
}
2407+
24002408
if (info != null && FunctionTypeBuilder.isFunctionTypeDeclaration(info)) {
24012409
String fnName = lValue.getQualifiedName();
24022410
return createFunctionTypeFromNodes(null, fnName, info, lValue);
@@ -2406,12 +2414,6 @@ && shouldUseFunctionLiteralType(
24062414
return getNativeType(JSTypeNative.NO_TYPE);
24072415
}
24082416

2409-
if (rValue != null && rValue.isAssign()) {
2410-
// Handle nested assignments. For example, tsickle generates code like this:
2411-
// let Foo = Foo_1 = tslib_1.decorate(...)
2412-
return getDeclaredType(info, lValue, rValue.getSecondChild(), null);
2413-
}
2414-
24152417
return null;
24162418
}
24172419

test/com/google/javascript/jscomp/CheckJsDocTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,4 +1287,14 @@ public void testMangleClosureModuleExportsContentsTypes() {
12871287
"/** @type {!Array<module$exports$foo$bar>} */ let x;",
12881288
"/** @type {!Array<UnrecognizedType_module$exports$foo$bar>} */ let x;");
12891289
}
1290+
1291+
@Test
1292+
public void testNameDeclarationAndAssignForAbstractClass() {
1293+
testSame(lines("/** @abstract */", "let A = A_1 = class A {}"));
1294+
}
1295+
1296+
@Test
1297+
public void testNameDeclarationAndAssignForTemplatedClass() {
1298+
testSame(lines("/** @template T */", "let A = A_1 = class A {}"));
1299+
}
12901300
}

test/com/google/javascript/jscomp/InlineAndCollapsePropertiesTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2525,6 +2525,53 @@ public void testClassStaticInheritance_cantDetermineSuperclass() {
25252525
"use(C.foo);"));
25262526
}
25272527

2528+
@Test
2529+
public void testTypeScriptDecoratedClass() {
2530+
// TypeScript 5.1 emits this for decorated classes.
2531+
test(
2532+
lines(
2533+
"let A = class A { static getId() { return A.ID; } };", //
2534+
"A.ID = \"a\";",
2535+
"A = tslib.__decorate([], A);",
2536+
"if (false) {",
2537+
" /** @const {string} */ A.ID;",
2538+
"}",
2539+
"console.log(A.getId());"),
2540+
lines(
2541+
"let A = class A$jscomp$1 { static getId() { return A$jscomp$1.ID; } };",
2542+
"A.ID = \"a\";",
2543+
"A = tslib.__decorate([], A);",
2544+
"if (false) {",
2545+
" /** @const */ A.ID;",
2546+
"}",
2547+
"console.log(A.getId());"));
2548+
}
2549+
2550+
@Test
2551+
public void testTypeScriptDecoratedClass_withExtraVarAssignment() {
2552+
// TypeScript 5.2 emits this for decorated classes, which reference static properties on
2553+
// themselves.
2554+
test(
2555+
lines(
2556+
"var A_1;", //
2557+
"let A = A_1 = class A { static getId() { return A_1.ID; } };",
2558+
"A.ID = \"a\";",
2559+
"A = A_1 = tslib.__decorate([], A);",
2560+
"if (false) {",
2561+
" /** @const {string} */ A.ID;",
2562+
"}",
2563+
"console.log(A.getId());"),
2564+
lines(
2565+
"var A_1;", //
2566+
"let A = A_1 = class A$jscomp$1 { static getId() { return A_1.ID; } };",
2567+
"A.ID = \"a\";",
2568+
"A = A_1 = tslib.__decorate([], A);",
2569+
"if (false) {",
2570+
" /** @const */ A.ID;",
2571+
"}",
2572+
"console.log(A.getId());"));
2573+
}
2574+
25282575
@Test
25292576
public void testAliasForSuperclassNamespace() {
25302577
test(

test/com/google/javascript/jscomp/NodeUtilTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3589,6 +3589,9 @@ public void testGetBestJsDocInfoForClasses() {
35893589

35903590
classNode = parseFirst(CLASS, "/** @export */ var Foo = class Bar {}");
35913591
assertThat(NodeUtil.getBestJSDocInfo(classNode).isExport()).isTrue();
3592+
3593+
classNode = parseFirst(CLASS, "var Foo_1; /** @export */ let Foo = Foo_1 = class Foo {}");
3594+
assertThat(NodeUtil.getBestJSDocInfo(classNode).isExport()).isTrue();
35923595
}
35933596

35943597
@Test

test/com/google/javascript/jscomp/TypedScopeCreatorTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2736,6 +2736,30 @@ public void testClassDeclarationWithExtends_andIncompatibleJsdoc() {
27362736
assertThat(foo.isAmbiguousConstructor()).isTrue();
27372737
}
27382738

2739+
@Test
2740+
public void testAbstractClassDeclarationWithExtends_nameDeclAndAssign() {
2741+
testSame(
2742+
lines(
2743+
"var Bar_1;", //
2744+
"/** @abstract */",
2745+
"let Bar = Bar_1 = class Bar {}",
2746+
"class Foo extends Bar {}"));
2747+
2748+
FunctionType bar1 = (FunctionType) findNameType("Bar_1", globalScope);
2749+
FunctionType bar = (FunctionType) findNameType("Bar", globalScope);
2750+
FunctionType foo = (FunctionType) findNameType("Foo", globalScope);
2751+
assertThat(bar.isAbstract()).isTrue();
2752+
assertThat(bar).isEqualTo(bar1);
2753+
assertType(foo.getInstanceType()).isSubtypeOf(bar.getInstanceType());
2754+
assertType(foo.getImplicitPrototype()).isEqualTo(bar);
2755+
assertScope(globalScope).declares("Bar").withTypeThat().isEqualTo(bar);
2756+
assertScope(globalScope).declares("Foo").withTypeThat().isEqualTo(foo);
2757+
2758+
assertThat(foo.getInstanceType().loosenTypecheckingDueToForwardReferencedSupertype()).isFalse();
2759+
assertThat(foo.loosenTypecheckingDueToForwardReferencedSupertype()).isFalse();
2760+
assertThat(foo.isAmbiguousConstructor()).isFalse();
2761+
}
2762+
27392763
@Test
27402764
public void testClassDeclarationWithExtendsFromNamespace() {
27412765
testSame(

0 commit comments

Comments
 (0)