From 407cb25bcf5c4afd23737ebc1414c04f84a31829 Mon Sep 17 00:00:00 2001 From: sdh Date: Fri, 6 Jul 2018 14:14:01 -0700 Subject: [PATCH] Validate dynamic extends clauses during TypeCheck Allows extending things other than qualified names, provided `@extends` is specified in the JSDoc. Emits a warning if no such annotation is given, or else checks that the extended type is in fact compatible with any declared supertype. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=203523284 --- .../google/javascript/jscomp/TypeCheck.java | 4 +- .../javascript/jscomp/TypeValidator.java | 38 +++++++- .../javascript/jscomp/TypedScopeCreator.java | 24 +++-- .../jscomp/TypeCheckNoTranspileTest.java | 90 ++++++++++++++----- 4 files changed, 123 insertions(+), 33 deletions(-) diff --git a/src/com/google/javascript/jscomp/TypeCheck.java b/src/com/google/javascript/jscomp/TypeCheck.java index 1148af2db8b..8e9e6df766c 100644 --- a/src/com/google/javascript/jscomp/TypeCheck.java +++ b/src/com/google/javascript/jscomp/TypeCheck.java @@ -2001,7 +2001,9 @@ private void visitClass(NodeTraversal t, Node n) { // Ensure that the `extends` clause is actually a constructor or interface. If it is, but // it's the wrong one then checkConstructor or checkInterface will warn. JSType superType = extendsClause.getJSType(); - if (!(superType.isConstructor() || superType.isInterface())) { + if (superType.isConstructor() || superType.isInterface()) { + validator.expectExtends(n, functionType, superType.toMaybeFunctionType()); + } else { compiler.report( t.makeError( n, diff --git a/src/com/google/javascript/jscomp/TypeValidator.java b/src/com/google/javascript/jscomp/TypeValidator.java index 3f977fcff56..178a8ebf596 100644 --- a/src/com/google/javascript/jscomp/TypeValidator.java +++ b/src/com/google/javascript/jscomp/TypeValidator.java @@ -678,8 +678,7 @@ void expectSuperType(NodeTraversal t, Node n, ObjectType superObject, superObject, declaredSuper, report(t.makeError(n, MISSING_EXTENDS_TAG_WARNING, subObject.toString()))); } else { - mismatch(n, "mismatch in declaration of superclass type", - superObject, declaredSuper); + mismatch(n, "mismatch in declaration of superclass type", superObject, declaredSuper); } // Correct the super type. @@ -689,6 +688,41 @@ void expectSuperType(NodeTraversal t, Node n, ObjectType superObject, } } + /** + * Expect that an ES6 class's extends clause is actually a supertype of the given class. + * + * @param n The node where warnings should point to. + * @param subCtor The sub constructor type. + * @param superCtor The expected super constructor. + */ + void expectExtends(Node n, FunctionType subCtor, FunctionType superCtor) { + if (superCtor == null || (!superCtor.isConstructor() && !superCtor.isInterface())) { + // toMaybeFunctionType failed, or we've got a loose type. Let it go for now. + return; + } + if (superCtor.isConstructor() != subCtor.isConstructor()) { + // Don't bother looking if one is a constructor and the other is an interface. + // We'll report an error elsewhere. + return; + } + ObjectType superInstance = superCtor.getInstanceType(); + if (subCtor.isConstructor()) { + // There should be exactly one superclass, and it needs to have this constructor. + ObjectType declaredSuper = subCtor.getSuperClassConstructor().getInstanceType(); + if (!superInstance.isEquivalentTo(declaredSuper)) { + mismatch(n, "mismatch in declaration of superclass type", superInstance, declaredSuper); + } + } else if (subCtor.isInterface()) { + // Find an equivalent constructor in the superinterfaces. There may have been multiple + // super-interfaces marked, but we can't know which was intended so just give the error + // on the first one. + if (!subCtor.explicitlyImplOrExtInterface(superCtor)) { + ObjectType extended = subCtor.getExtendedInterfaces().get(0); + mismatch(n, "mismatch in declaration of superclass type", superInstance, extended); + } + } + } + /** * Expect that it's valid to assign something to a given type's prototype. * diff --git a/src/com/google/javascript/jscomp/TypedScopeCreator.java b/src/com/google/javascript/jscomp/TypedScopeCreator.java index dca2804dbf3..481bf00353f 100644 --- a/src/com/google/javascript/jscomp/TypedScopeCreator.java +++ b/src/com/google/javascript/jscomp/TypedScopeCreator.java @@ -155,6 +155,12 @@ final class TypedScopeCreator implements ScopeCreator, StaticSymbolTable