Skip to content

Commit

Permalink
Report implicit any error for 'yield' result with no contextual type
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Oct 31, 2020
1 parent e044b56 commit 074d19b
Show file tree
Hide file tree
Showing 13 changed files with 490 additions and 6 deletions.
13 changes: 11 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30635,8 +30635,17 @@ namespace ts {
return getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Next, returnType, isAsync)
|| anyType;
}

return getContextualIterationType(IterationTypeKind.Next, func) || anyType;
let type = getContextualIterationType(IterationTypeKind.Next, func);
if (!type) {
type = anyType;
if (produceDiagnostics && noImplicitAny && !expressionResultIsUnused(node)) {
const contextualType = getContextualType(node);
if (!contextualType || isTypeAny(contextualType)) {
error(node, Diagnostics.yield_expression_implicitly_results_in_an_any_type_because_its_containing_generator_lacks_a_return_type_annotation);
}
}
}
return type;
}

function checkConditionalExpression(node: ConditionalExpression, checkMode?: CheckMode): Type {
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4948,6 +4948,10 @@
"category": "Error",
"code": 7056
},
"'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.": {
"category": "Error",
"code": 7057
},

"You cannot rename this element.": {
"category": "Error",
Expand Down
38 changes: 38 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6973,4 +6973,42 @@ namespace ts {
return bindParentToChildIgnoringJSDoc(child, parent) || bindJSDoc(child);
}
}

/**
* Indicates whether the result of an `Expression` will be unused.
*
* NOTE: This requires a node with a valid `parent` pointer.
*/
export function expressionResultIsUnused(node: Expression): boolean {
Debug.assertIsDefined(node.parent);
while (true) {
const parent: Node = node.parent;
// walk up parenthesized expressions, but keep a pointer to the top-most parenthesized expression
if (isParenthesizedExpression(parent)) {
node = parent;
continue;
}
// result is unused in an expression statement, `void` expression, or the initializer or incrementer of a `for` loop
if (isExpressionStatement(parent) ||
isVoidExpression(parent) ||
isForStatement(parent) && (parent.initializer === node || parent.incrementor === node)) {
return true;
}
if (isCommaListExpression(parent)) {
// left side of comma is always unused
if (node !== last(parent.elements)) return true;
// right side of comma is unused if parent is unused
node = parent;
continue;
}
if (isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.CommaToken) {
// left side of comma is always unused
if (node === parent.left) return true;
// right side of comma is unused if parent is unused
node = parent;
continue;
}
return false;
}
}
}
40 changes: 40 additions & 0 deletions tests/baselines/reference/generatorImplicitAny.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
tests/cases/conformance/generators/generatorImplicitAny.ts(8,19): error TS7057: 'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.
tests/cases/conformance/generators/generatorImplicitAny.ts(26,7): error TS7057: 'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.


==== tests/cases/conformance/generators/generatorImplicitAny.ts (2 errors) ====
function* g() {}

// https://github.com/microsoft/TypeScript/issues/35105
declare function noop(): void;
declare function f<T>(value: T): void;

function* g2() {
const value = yield; // error: implicit any
~~~~~
!!! error TS7057: 'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.
}

function* g3() {
const value: string = yield; // ok, contextually typed by `value`.
}

function* g4() {
yield; // ok, result is unused
yield, noop(); // ok, result is unused
noop(), yield, noop(); // ok, result is unused
(yield); // ok, result is unused
(yield, noop()), noop(); // ok, result is unused
for(yield; false; yield); // ok, results are unused
void (yield); // ok, results are unused
}

function* g5() {
f(yield); // error: implicit any
~~~~~
!!! error TS7057: 'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.
}

function* g6() {
f<string>(yield); // ok, contextually typed by f<string>
}
54 changes: 53 additions & 1 deletion tests/baselines/reference/generatorImplicitAny.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,57 @@
//// [generatorImplicitAny.ts]
function* g() {}
function* g() {}

// https://github.com/microsoft/TypeScript/issues/35105
declare function noop(): void;
declare function f<T>(value: T): void;

function* g2() {
const value = yield; // error: implicit any
}

function* g3() {
const value: string = yield; // ok, contextually typed by `value`.
}

function* g4() {
yield; // ok, result is unused
yield, noop(); // ok, result is unused
noop(), yield, noop(); // ok, result is unused
(yield); // ok, result is unused
(yield, noop()), noop(); // ok, result is unused
for(yield; false; yield); // ok, results are unused
void (yield); // ok, results are unused
}

function* g5() {
f(yield); // error: implicit any
}

function* g6() {
f<string>(yield); // ok, contextually typed by f<string>
}

//// [generatorImplicitAny.js]
function* g() { }
function* g2() {
const value = yield; // error: implicit any
}
function* g3() {
const value = yield; // ok, contextually typed by `value`.
}
function* g4() {
yield; // ok, result is unused
yield, noop(); // ok, result is unused
noop(), yield, noop(); // ok, result is unused
(yield); // ok, result is unused
(yield, noop()), noop(); // ok, result is unused
for (yield; false; yield)
; // ok, results are unused
void (yield); // ok, results are unused
}
function* g5() {
f(yield); // error: implicit any
}
function* g6() {
f(yield); // ok, contextually typed by f<string>
}
57 changes: 57 additions & 0 deletions tests/baselines/reference/generatorImplicitAny.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,60 @@
function* g() {}
>g : Symbol(g, Decl(generatorImplicitAny.ts, 0, 0))

// https://github.com/microsoft/TypeScript/issues/35105
declare function noop(): void;
>noop : Symbol(noop, Decl(generatorImplicitAny.ts, 0, 16))

declare function f<T>(value: T): void;
>f : Symbol(f, Decl(generatorImplicitAny.ts, 3, 30))
>T : Symbol(T, Decl(generatorImplicitAny.ts, 4, 19))
>value : Symbol(value, Decl(generatorImplicitAny.ts, 4, 22))
>T : Symbol(T, Decl(generatorImplicitAny.ts, 4, 19))

function* g2() {
>g2 : Symbol(g2, Decl(generatorImplicitAny.ts, 4, 38))

const value = yield; // error: implicit any
>value : Symbol(value, Decl(generatorImplicitAny.ts, 7, 9))
}

function* g3() {
>g3 : Symbol(g3, Decl(generatorImplicitAny.ts, 8, 1))

const value: string = yield; // ok, contextually typed by `value`.
>value : Symbol(value, Decl(generatorImplicitAny.ts, 11, 9))
}

function* g4() {
>g4 : Symbol(g4, Decl(generatorImplicitAny.ts, 12, 1))

yield; // ok, result is unused
yield, noop(); // ok, result is unused
>noop : Symbol(noop, Decl(generatorImplicitAny.ts, 0, 16))

noop(), yield, noop(); // ok, result is unused
>noop : Symbol(noop, Decl(generatorImplicitAny.ts, 0, 16))
>noop : Symbol(noop, Decl(generatorImplicitAny.ts, 0, 16))

(yield); // ok, result is unused
(yield, noop()), noop(); // ok, result is unused
>noop : Symbol(noop, Decl(generatorImplicitAny.ts, 0, 16))
>noop : Symbol(noop, Decl(generatorImplicitAny.ts, 0, 16))

for(yield; false; yield); // ok, results are unused
void (yield); // ok, results are unused
}

function* g5() {
>g5 : Symbol(g5, Decl(generatorImplicitAny.ts, 22, 1))

f(yield); // error: implicit any
>f : Symbol(f, Decl(generatorImplicitAny.ts, 3, 30))
}

function* g6() {
>g6 : Symbol(g6, Decl(generatorImplicitAny.ts, 26, 1))

f<string>(yield); // ok, contextually typed by f<string>
>f : Symbol(f, Decl(generatorImplicitAny.ts, 3, 30))
}
87 changes: 87 additions & 0 deletions tests/baselines/reference/generatorImplicitAny.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,90 @@
function* g() {}
>g : () => Generator<never, void, unknown>

// https://github.com/microsoft/TypeScript/issues/35105
declare function noop(): void;
>noop : () => void

declare function f<T>(value: T): void;
>f : <T>(value: T) => void
>value : T

function* g2() {
>g2 : () => Generator<undefined, void, unknown>

const value = yield; // error: implicit any
>value : any
>yield : any
}

function* g3() {
>g3 : () => Generator<undefined, void, string>

const value: string = yield; // ok, contextually typed by `value`.
>value : string
>yield : any
}

function* g4() {
>g4 : () => Generator<undefined, void, unknown>

yield; // ok, result is unused
>yield : any

yield, noop(); // ok, result is unused
>yield, noop() : void
>yield : any
>noop() : void
>noop : () => void

noop(), yield, noop(); // ok, result is unused
>noop(), yield, noop() : void
>noop(), yield : any
>noop() : void
>noop : () => void
>yield : any
>noop() : void
>noop : () => void

(yield); // ok, result is unused
>(yield) : any
>yield : any

(yield, noop()), noop(); // ok, result is unused
>(yield, noop()), noop() : void
>(yield, noop()) : void
>yield, noop() : void
>yield : any
>noop() : void
>noop : () => void
>noop() : void
>noop : () => void

for(yield; false; yield); // ok, results are unused
>yield : any
>false : false
>yield : any

void (yield); // ok, results are unused
>void (yield) : undefined
>(yield) : any
>yield : any
}

function* g5() {
>g5 : () => Generator<undefined, void, any>

f(yield); // error: implicit any
>f(yield) : void
>f : <T>(value: T) => void
>yield : any
}

function* g6() {
>g6 : () => Generator<undefined, void, string>

f<string>(yield); // ok, contextually typed by f<string>
>f<string>(yield) : void
>f : <T>(value: T) => void
>yield : any
}
Loading

0 comments on commit 074d19b

Please sign in to comment.