From 2858771a54b7c426b5131fd3e47bdf93f2a3a6f2 Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Mon, 23 Feb 2015 19:07:29 -0800 Subject: [PATCH] Check iterable structure of the right hand side of 'for...of' statement --- src/compiler/checker.ts | 24 +++++++++++++++---- .../diagnosticInformationMap.generated.ts | 3 +++ src/compiler/diagnosticMessages.json | 12 ++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5a9e9575bca..efd101d8c06 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8768,7 +8768,7 @@ module ts { var leftType = checkExpression(varExpr); checkReferenceExpression(varExpr, Diagnostics.Invalid_left_hand_side_in_for_of_statement, Diagnostics.The_left_hand_side_of_a_for_of_statement_cannot_be_a_previously_defined_constant); var rightType = checkExpression(node.expression); - var iteratedType = getIteratedType(rightType); + var iteratedType = getIteratedType(rightType, node.expression); checkTypeAssignableTo(iteratedType, leftType, varExpr, /*headMessage*/ undefined); } @@ -8831,7 +8831,7 @@ module ts { var links = getNodeLinks(variable); if (!links.resolvedType) { links.resolvedType = resolvingType; - var type = getIteratedType(getTypeOfExpression(forOfStatement.expression)); + var type = getIteratedType(getTypeOfExpression(forOfStatement.expression), forOfStatement.expression); if (links.resolvedType === resolvingType) { links.resolvedType = type; } @@ -8843,7 +8843,7 @@ module ts { return links.resolvedType; } - function getIteratedType(iterable: Type): Type { + function getIteratedType(iterable: Type, expressionForError: Expression): Type { Debug.assert(languageVersion >= ScriptTarget.ES6); if (allConstituentTypesHaveKind(iterable, TypeFlags.Any)) { return anyType; @@ -8867,14 +8867,28 @@ module ts { // T is the type we are after. At every level that involves analyzing return types // of signatures, we union the return types of all the signatures. var iteratorFunction = getTypeOfPropertyOfType(iterable, getPropertyNameForKnownSymbolName("iterator")); - var iteratorFunctionSignatures = getSignaturesOfType(iteratorFunction, SignatureKind.Call); + var iteratorFunctionSignatures = iteratorFunction ? getSignaturesOfType(iteratorFunction, SignatureKind.Call) : emptyArray; + if (iteratorFunctionSignatures.length === 0) { + error(expressionForError, Diagnostics.The_right_hand_side_of_a_for_of_statement_must_have_a_Symbol_iterator_method_that_returns_an_iterator); + return unknownType; + } + var iterator = getUnionType(map(iteratorFunctionSignatures, getReturnTypeOfSignature)); var iteratorNextFunction = getTypeOfPropertyOfType(iterator, "next"); - var iteratorNextFunctionSignatures = getSignaturesOfType(iteratorNextFunction, SignatureKind.Call); + var iteratorNextFunctionSignatures = iteratorNextFunction ? getSignaturesOfType(iteratorNextFunction, SignatureKind.Call) : emptyArray; + if (iteratorNextFunctionSignatures.length === 0) { + error(expressionForError, Diagnostics.The_iterator_returned_by_the_right_hand_side_of_a_for_of_statement_must_have_a_next_method); + return unknownType; + } + var iteratorNextResult = getUnionType(map(iteratorNextFunctionSignatures, getReturnTypeOfSignature)); var iteratorNextValue = getTypeOfPropertyOfType(iteratorNextResult, "value"); + if (!iteratorNextValue) { + error(expressionForError, Diagnostics.The_object_returned_by_the_next_method_of_the_iterator_must_have_a_value_property); + return unknownType; + } return iteratorNextValue; // TODO diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 35b5ca5cdc3..08de6681226 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -329,6 +329,9 @@ module ts { The_left_hand_side_of_a_for_of_statement_cannot_be_a_previously_defined_constant: { code: 2485, category: DiagnosticCategory.Error, key: "The left-hand side of a 'for...of' statement cannot be a previously defined constant." }, The_left_hand_side_of_a_for_in_statement_cannot_be_a_previously_defined_constant: { code: 2486, category: DiagnosticCategory.Error, key: "The left-hand side of a 'for...in' statement cannot be a previously defined constant." }, Invalid_left_hand_side_in_for_of_statement: { code: 2487, category: DiagnosticCategory.Error, key: "Invalid left-hand side in 'for...of' statement." }, + The_right_hand_side_of_a_for_of_statement_must_have_a_Symbol_iterator_method_that_returns_an_iterator: { code: 2488, category: DiagnosticCategory.Error, key: "The right-hand side of a 'for...of' statement must have a '[Symbol.iterator]()' method that returns an iterator." }, + The_iterator_returned_by_the_right_hand_side_of_a_for_of_statement_must_have_a_next_method: { code: 2489, category: DiagnosticCategory.Error, key: "The iterator returned by the right-hand side of a 'for...of' statement must have a 'next()' method." }, + The_object_returned_by_the_next_method_of_the_iterator_must_have_a_value_property: { code: 2490, category: DiagnosticCategory.Error, key: "The object returned by the next method of the iterator must have a 'value' property." }, Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." }, Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." }, Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8b57778a696..45661c570c2 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1308,6 +1308,18 @@ "category": "Error", "code": 2487 }, + "The right-hand side of a 'for...of' statement must have a '[Symbol.iterator]()' method that returns an iterator.": { + "category": "Error", + "code": 2488 + }, + "The iterator returned by the right-hand side of a 'for...of' statement must have a 'next()' method.": { + "category": "Error", + "code": 2489 + }, + "The object returned by the next method of the iterator must have a 'value' property.": { + "category": "Error", + "code": 2490 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error",