Skip to content

Commit c7547ba

Browse files
Closure Teamcopybara-github
authored andcommitted
Modify TypedScopeCreator to not throw an error when there's a getter or member function in a public field.
PiperOrigin-RevId: 550633891
1 parent 17dbac3 commit c7547ba

File tree

2 files changed

+114
-13
lines changed

2 files changed

+114
-13
lines changed

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

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -789,8 +789,7 @@ CompilerInput getCompilerInput() {
789789
return compiler.getInput(inputId);
790790
}
791791

792-
@Nullable
793-
Module getModule() {
792+
@Nullable Module getModule() {
794793
return this.currentScope.getModule();
795794
}
796795

@@ -2358,8 +2357,7 @@ private TypedScope getLValueRootScope(Node n) {
23582357
* @param declaredRValueTypeSupplier A supplier for the declared type of the rvalue, used for
23592358
* destructuring declarations where we have to do additional work on the rvalue.
23602359
*/
2361-
@Nullable
2362-
JSType getDeclaredType(
2360+
@Nullable JSType getDeclaredType(
23632361
JSDocInfo info,
23642362
Node lValue,
23652363
@Nullable Node rValue,
@@ -3244,7 +3242,7 @@ void visitPostorder(NodeTraversal t, Node n, Node parent) {
32443242

32453243
/**
32463244
* Scope builder subclass for function scopes, which only contain bleeding function names and
3247-
* parameter names. The main function body is handled by the a NormalScopeBuilder on the function
3245+
* parameter names. The main function body is handled by the NormalScopeBuilder on the function
32483246
* block.
32493247
*/
32503248
private final class FunctionScopeBuilder extends AbstractScopeBuilder {
@@ -3523,11 +3521,13 @@ void visitPostorder(NodeTraversal t, Node n, Node parent) {
35233521
.defineSlot();
35243522
} else if (NodeUtil.isEs6ConstructorMemberFunctionDef(n)) {
35253523
// Ignore "constructor" since it has special handling in `createClassTypeFromNodes()`.
3526-
} else if (n.isMemberFunctionDef()) {
3524+
} else if (n.isMemberFunctionDef() && parent.isClassMembers()) {
35273525
defineMemberFunction(n);
35283526
} else if (n.isMemberFieldDef()) {
3527+
// public fields are roots of their own scope so the parent doesn't get passed into
3528+
// visitPostorder
35293529
defineMemberField(n);
3530-
} else if (n.isGetterDef() || n.isSetterDef()) {
3530+
} else if ((n.isGetterDef() || n.isSetterDef()) && parent.isClassMembers()) {
35313531
defineGetterSetter(n);
35323532
}
35333533
}
@@ -3594,8 +3594,10 @@ void defineGetterSetter(Node n) {
35943594
* nonstatic member.
35953595
*/
35963596
private ObjectType determineOwnerTypeForClassMember(Node member) {
3597-
// MEMBER_FUNCTION_DEF -> CLASS_MEMBERS -> CLASS or
3598-
// GETTER_DEF -> CLASS_MEMBERS -> CLASS
3597+
// MEMBER_FUNCTION_DEF -> CLASS_MEMBERS -> CLASS or
3598+
// MEMBER_FIELD_DEF -> CLASS_MEMBERS -> CLASS or
3599+
// GETTER_DEF -> CLASS_MEMBERS -> CLASS or
3600+
// SETTER_DEF -> CLASS_MEMBERS -> CLASS
35993601
Node ownerNode = member.getGrandparent();
36003602
checkState(ownerNode.isClass());
36013603
FunctionType ownerType = ownerNode.getJSType().toMaybeFunctionType();

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

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3273,11 +3273,11 @@ public void testClassDeclarationWithFieldThisAndSuper() {
32733273
" /** @type {number} */",
32743274
" a = 2;",
32753275
" /** @type {number} */",
3276-
" b = this.b;",
3276+
" b = this.a;",
32773277
"}",
32783278
"class Bar extends Foo {",
32793279
" /** @type {number} */",
3280-
" c = super.a + 1;",
3280+
" c = this.a + 1;", // Must use `this` and not `super` to access field of parent class
32813281
"}"));
32823282

32833283
FunctionType fooCtor = globalScope.getVar("Foo").getType().assertFunctionType();
@@ -3368,11 +3368,12 @@ public void testClassDeclarationWithStaticFieldThisAndSuper() {
33683368
" /** @type {number} */",
33693369
" static a = 2;",
33703370
" /** @type {number} */",
3371-
" static b = this.b;",
3371+
" static b = this.a;",
33723372
"}",
33733373
"class Bar extends Foo {",
33743374
" /** @type {number} */",
3375-
" static c = super.a + 1;",
3375+
" static c = this.a + 1;", // Must use `this` and not `super` to access field of parent
3376+
// class
33763377
"}"));
33773378

33783379
FunctionType fooCtor = globalScope.getVar("Foo").getType().assertFunctionType();
@@ -3391,6 +3392,104 @@ public void testClassDeclarationWithStaticFieldThisAndSuper() {
33913392
assertType(barCtor.getPropertyType("c")).isNumber();
33923393
}
33933394

3395+
@Test
3396+
public void testTypeOfThisInObjectLitInPublicField() {
3397+
// Make sure the `this` on the RHS of a class field declaration has the appropriate type, even
3398+
// when it appears within an object literal
3399+
testSame(
3400+
lines(
3401+
"class Foo {", //
3402+
" x = 3;",
3403+
" bar = {",
3404+
" b: this.x",
3405+
" };",
3406+
"}"));
3407+
3408+
FunctionType fooCtor = globalScope.getVar("Foo").getType().assertFunctionType();
3409+
ObjectType fooInstance = fooCtor.getInstanceType();
3410+
Node getProp =
3411+
fooInstance
3412+
.getPropertyNode("bar")
3413+
.getOnlyChild() // OBJECTLIT
3414+
.getOnlyChild() // STRING_KEY
3415+
.getOnlyChild(); // GETPROP
3416+
assertNode(getProp).hasJSTypeThat().isNumber();
3417+
Node thisNode = getProp.getOnlyChild();
3418+
assertNode(thisNode).hasJSTypeThat().isEqualTo(fooInstance);
3419+
3420+
testSame(
3421+
lines(
3422+
"class Foo {", //
3423+
" static baz = {",
3424+
" e: this",
3425+
" };",
3426+
"}"));
3427+
3428+
fooCtor = globalScope.getVar("Foo").getType().assertFunctionType();
3429+
thisNode =
3430+
fooCtor
3431+
.getPropertyNode("baz")
3432+
.getOnlyChild() // OBJECTLIT
3433+
.getOnlyChild() // STRING_KEY
3434+
.getOnlyChild(); // THIS
3435+
assertNode(thisNode).hasJSTypeThat().isEqualTo(fooCtor);
3436+
}
3437+
3438+
@Test
3439+
public void testGetterInObjectLitInPublicField() {
3440+
// Previously a bug caused getters & setters in object literals on the RHS of a field
3441+
// declaration to be treated as if they belonged to the class.
3442+
//
3443+
// Make sure that `this` in an object literal getter or setter has the unknown type.
3444+
testSame(
3445+
lines(
3446+
"class Foo {", //
3447+
" bar = {",
3448+
" get baz() {",
3449+
" X: this;",
3450+
" }",
3451+
" };",
3452+
"}"));
3453+
Node thisNode = getLabeledStatement("X").statementNode.getOnlyChild();
3454+
// We don't create unique types for object literals, so even though we know
3455+
// the getter will (probably) only be called on this object, we just
3456+
// use unknown for the type of `this`.
3457+
assertNode(thisNode).hasJSTypeThat().isUnknown();
3458+
3459+
testSame(
3460+
lines(
3461+
"class Foo {", //
3462+
" static bar = {",
3463+
" /** @this {string} */",
3464+
" get baz() { Y: this; }",
3465+
" };",
3466+
"}"));
3467+
3468+
thisNode = getLabeledStatement("Y").statementNode.getOnlyChild();
3469+
assertNode(thisNode).hasJSTypeThat().isString();
3470+
}
3471+
3472+
@Test
3473+
public void testMemberFunctionInObjectLitInPublicField() {
3474+
// Previously a bug caused methods in object literals on the RHS of a field declaration to be
3475+
// treated as if they were methods on the class.
3476+
//
3477+
// Make sure that `this` in an object literal method has the unknown type.
3478+
testSame(
3479+
lines(
3480+
"class Foo {", //
3481+
" bar = {",
3482+
" /** @this {number} */",
3483+
" member() {",
3484+
" X: this;",
3485+
" }",
3486+
" };",
3487+
"}"));
3488+
3489+
Node thisNode = getLabeledStatement("X").statementNode.getOnlyChild();
3490+
assertNode(thisNode).hasJSTypeThat().isNumber();
3491+
}
3492+
33943493
@Test
33953494
public void testClassDeclarationWithDeprecatedField() {
33963495
testSame(

0 commit comments

Comments
 (0)