From 80e120bdf4f723407a0aee50bc6b709ffcf96d4e Mon Sep 17 00:00:00 2001 From: dimvar Date: Wed, 7 Feb 2018 10:01:48 -0800 Subject: [PATCH] Roll forward of 20b4233e5b136cd5c226df594423ca271ce1f7fb NEW: added two more unit tests. No code changes; the fix was in the originally broken target. Automated g4 rollback of changelist 184218181. *** Reason for rollback *** Fixed jscontainer. *** Original change description *** Fix bug in the old type checker where a stub method definition causes loss of type checking. *** ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=184853474 --- .../javascript/jscomp/TypedScopeCreator.java | 24 ++++++- .../javascript/jscomp/TypeCheckTest.java | 72 +++++++++++++++++++ 2 files changed, 93 insertions(+), 3 deletions(-) diff --git a/src/com/google/javascript/jscomp/TypedScopeCreator.java b/src/com/google/javascript/jscomp/TypedScopeCreator.java index 46775c9c679..a7371582aeb 100644 --- a/src/com/google/javascript/jscomp/TypedScopeCreator.java +++ b/src/com/google/javascript/jscomp/TypedScopeCreator.java @@ -1806,6 +1806,22 @@ private ObjectType getObjectSlot(String slotName) { return null; } + /** + * When a class has a stub for a property, and the property exists on a super interface, + * use that type. + */ + private JSType getInheritedInterfacePropertyType(ObjectType obj, String propName) { + if (obj != null && obj.isPrototypeObject()) { + FunctionType f = obj.getOwnerFunction(); + for (ObjectType i : f.getImplementedInterfaces()) { + if (i.hasProperty(propName)) { + return i.getPropertyType(propName); + } + } + } + return null; + } + /** * Resolve any stub declarations to unknown types if we could not * find types for them during traversal. @@ -1826,17 +1842,19 @@ void resolveStubDeclarations() { // If we see a stub property, make sure to register this property // in the type registry. ObjectType ownerType = getObjectSlot(ownerName); - defineSlot(n, parent, unknownType, true); + JSType inheritedType = getInheritedInterfacePropertyType(ownerType, propName); + JSType stubType = inheritedType == null ? unknownType : inheritedType; + defineSlot(n, parent, stubType, true); if (ownerType != null && (isExtern || ownerType.isFunctionPrototypeType())) { // If this is a stub for a prototype, just declare it // as an unknown type. These are seen often in externs. ownerType.defineInferredProperty( - propName, unknownType, n); + propName, stubType, n); } else { typeRegistry.registerPropertyOnType( - propName, ownerType == null ? unknownType : ownerType); + propName, ownerType == null ? stubType : ownerType); } } } diff --git a/test/com/google/javascript/jscomp/TypeCheckTest.java b/test/com/google/javascript/jscomp/TypeCheckTest.java index aa7641b5462..efbac7b7899 100644 --- a/test/com/google/javascript/jscomp/TypeCheckTest.java +++ b/test/com/google/javascript/jscomp/TypeCheckTest.java @@ -2951,6 +2951,78 @@ public void testStubFunctionDeclaration10() { "function(number): number"); } + + public void testStubMethodDeclarationDoesntBlockTypechecking_1() { + testTypes( + lines( + "/** @interface */", + "function Foo() {}", + "/** @return {number} */", + "Foo.prototype.method = function() {};", + "/**", + " * @constructor", + " * @implements {Foo}", + " */", + "function Bar() {}", + "Bar.prototype.method;", + "var /** null */ n = (new Bar).method();"), + lines( + "initializing variable", + "found : number", + "required: null")); + } + + public void testStubMethodDeclarationDoesntBlockTypechecking_2() { + testTypes( + lines( + "/** @constructor */", + "function Foo() {}", + "/** @return {number} */", + "Foo.prototype.method = function() {};", + "/**", + " * @constructor", + " * @extends {Foo}", + " */", + "function Bar() {}", + "Bar.prototype.method;", + "var /** null */ n = (new Bar).method();"), + lines( + "initializing variable", + "found : number", + "required: null")); + } + + public void testStubMethodDeclarationDoesntBlockTypechecking_3() { + testTypes( + lines( + "/** @interface */", + "var Foo = function() {};", + "/** @type {number} */", + "Foo.prototype.num;", + "/**", + " * @constructor", + " * @implements {Foo}", + " */", + "var Bar = function() {};", + "/** @type {?} */", + "Bar.prototype.num;", + "var /** string */ x = (new Bar).num;")); + } + + public void testStubMethodDeclarationDoesntBlockTypechecking_4() { + testTypes( + lines( + "/** @interface */", + "class Foo {}", + "/** @return {number} */", + "Foo.prototype.num;", + "/** @implements {Foo} */", + "class Bar {", + " get num() { return 1; }", + "}", + "var /** string */ x = (new Bar).num;")); + } + public void testNestedFunctionInference1() { String nestedAssignOfFooAndBar = "/** @constructor */ function f() {};" +