Skip to content

Commit

Permalink
Typecheck returns with children in generators.
Browse files Browse the repository at this point in the history
We still don't typecheck returns with no children:
/** @return {!Generator<number>} */
function *gen() {
  return;
}

but now raise an error on returns with children that don't typecheck, like this:

/** @return {!Generator<number>} */
function *gen() {
  return 'foobar';
}

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=189815295
  • Loading branch information
lauraharker authored and brad4d committed Mar 21, 2018
1 parent d418d44 commit cc198bd
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 26 deletions.
62 changes: 38 additions & 24 deletions src/com/google/javascript/jscomp/TypeCheck.java
Expand Up @@ -2080,24 +2080,28 @@ private void visitParameterList(NodeTraversal t, Node call,
*/
private void visitReturn(NodeTraversal t, Node n) {
Node enclosingFunction = t.getEnclosingFunction();
if (enclosingFunction.isGeneratorFunction() && !n.hasChildren()) {
// Allow "return;" in a generator function, even if it's not the declared return type.
// e.g. Don't warn for a generator function with JSDoc "@return {!Generator<number>}" and
// a "return;" in the fn body, even though "undefined" does not match "number".
return;
}

JSType jsType = getJSType(enclosingFunction);

if (jsType.isFunctionType()) {
FunctionType functionType = jsType.toMaybeFunctionType();
if (enclosingFunction.isGeneratorFunction()) {
// Allow "return;" in a generator function, even if it's not the declared return type.
// e.g. Don't warn for a generator function with JSDoc "@return {!Generator<number>}" and
// a "return;" in the fn body, even though "undefined" does not match "number".
// TODO(b/73966409): Typecheck "return [expr]" in generator fns, just not "return;"
return;
}

JSType returnType = functionType.getReturnType();

// if no return type is specified, undefined must be returned
// (it's a void function)
if (returnType == null) {
returnType = getNativeType(VOID_TYPE);
} else if (enclosingFunction.isGeneratorFunction()) {
// Unwrap the template variable from a generator function's declared return type.
// e.g. if returnType is "Generator<string>", make it just "string".
returnType = getTemplateTypeOfGenerator(returnType);
}

// fetching the returned value's type
Expand All @@ -2123,23 +2127,8 @@ private void visitYield(NodeTraversal t, Node n) {
JSType declaredYieldType = getNativeType(UNKNOWN_TYPE);
if (jsType.isFunctionType()) {
FunctionType functionType = jsType.toMaybeFunctionType();
ObjectType dereferencedReturnType = functionType.getReturnType().dereference();
if (dereferencedReturnType != null) {
TemplateTypeMap templateTypeMap = dereferencedReturnType.getTemplateTypeMap();
if (templateTypeMap.hasTemplateKey(typeRegistry.getIterableTemplate())) {
// Generator JSDoc says
// @return {!Iterable<SomeElementType>}
// or
// @return {!Generator<SomeElementType>}
declaredYieldType =
templateTypeMap.getResolvedTemplateType(typeRegistry.getIterableTemplate());
} else if (templateTypeMap.hasTemplateKey(typeRegistry.getIteratorTemplate())) {
// Generator JSDoc says
// @return {!Iterator<SomeElementType>}
declaredYieldType =
templateTypeMap.getResolvedTemplateType(typeRegistry.getIteratorTemplate());
}
}
JSType returnType = functionType.getReturnType();
declaredYieldType = getTemplateTypeOfGenerator(returnType);
}

// fetching the yielded value's type
Expand Down Expand Up @@ -2174,6 +2163,31 @@ private void visitYield(NodeTraversal t, Node n) {
"Yielded type does not match declared return type.");
}

/**
* Returns the given type's resolved template type corresponding to the corresponding to the
* Generator, Iterable or Iterator template key if possible.
*
* If the given type is not an Iterator or Iterable, returns the unknown type..
*/
private JSType getTemplateTypeOfGenerator(JSType generator) {
ObjectType dereferencedType = generator.dereference();
if (dereferencedType != null) {
TemplateTypeMap templateTypeMap = dereferencedType.getTemplateTypeMap();
if (templateTypeMap.hasTemplateKey(typeRegistry.getIterableTemplate())) {
// Generator JSDoc says
// @return {!Iterable<SomeElementType>}
// or
// @return {!Generator<SomeElementType>}
return templateTypeMap.getResolvedTemplateType(typeRegistry.getIterableTemplate());
} else if (templateTypeMap.hasTemplateKey(typeRegistry.getIteratorTemplate())) {
// Generator JSDoc says
// @return {!Iterator<SomeElementType>}
return templateTypeMap.getResolvedTemplateType(typeRegistry.getIteratorTemplate());
}
}
return getNativeType(UNKNOWN_TYPE);
}

/**
* This function unifies the type checking involved in the core binary
* operators and the corresponding assignment operators. The representation
Expand Down
Expand Up @@ -359,8 +359,11 @@ public void testGenerator_return1() {
}

public void testGenerator_return2() {
// TODO(b/73966409): Emit a type mismatch warning here.
testTypes("/** @return {!Generator<string>} */ function *gen() { return 1; }");
testTypes("/** @return {!Generator<string>} */ function *gen() { return 1; }",
lines(
"inconsistent return type",
"found : number",
"required: string"));
}

public void testGenerator_return3() {
Expand Down

0 comments on commit cc198bd

Please sign in to comment.