diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 270de264ad5..8bc84469c6a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8023,9 +8023,27 @@ module ts { } } + function isYieldExpressionInClass(node: YieldExpression): boolean { + let current: Node = node + let parent = node.parent; + while (parent) { + if (isFunctionLike(parent) && current === (parent).body) { + return false; + } + else if (current.kind === SyntaxKind.ClassDeclaration || current.kind === SyntaxKind.ClassExpression) { + return true; + } + + current = parent; + parent = parent.parent; + } + + return false; + } + function checkYieldExpression(node: YieldExpression): Type { // Grammar checking - if (!(node.parserContextFlags & ParserContextFlags.Yield)) { + if (!(node.parserContextFlags & ParserContextFlags.Yield) || isYieldExpressionInClass(node)) { grammarErrorOnFirstToken(node, Diagnostics.A_yield_expression_is_only_allowed_in_a_generator_declaration); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 055fc1b6f74..e70597cb432 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4010,7 +4010,20 @@ module ts { property.name = name; property.questionToken = questionToken; property.type = parseTypeAnnotation(); - property.initializer = allowInAnd(parseNonParameterInitializer); + + // For initializers, we always want to allow 'in' expressions. For instance properties specifically, + // since they are evaluated inside the constructor, we do *not* want to parse yield expressions, + // so we specifically turn the yield context off. The grammar would look something like this: + // + // MemberVariableDeclaration[Yield]: + // AccessibilityModifier_opt PropertyName TypeAnnotation_opt Initialiser_opt[In]; + // AccessibilityModifier_opt static_opt PropertyName TypeAnnotation_opt Initialiser_opt[In, ?Yield]; + // + // The checker may still error in the static case to explicitly disallow the yield expression. + property.initializer = modifiers && modifiers.flags & NodeFlags.Static + ? allowInAnd(parseNonParameterInitializer) + : doOutsideOfContext(ParserContextFlags.Yield | ParserContextFlags.DisallowIn, parseNonParameterInitializer); + parseSemicolon(); return finishNode(property); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 14968210a0b..98864b20d85 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -530,11 +530,6 @@ module ts { return traverse(body); function traverse(node: Node): void { - // Yield expressions may occur in decorators - if (node.decorators) { - forEach(node.decorators, traverse); - } - switch (node.kind) { case SyntaxKind.YieldExpression: visitor(node); @@ -546,18 +541,17 @@ module ts { case SyntaxKind.InterfaceDeclaration: case SyntaxKind.ModuleDeclaration: case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.ClassDeclaration: // These are not allowed inside a generator now, but eventually they may be allowed // as local types. Regardless, any yield statements contained within them should be // skipped in this traversal. return; - case SyntaxKind.ClassDeclaration: - // A class declaration/expression may extend a yield expression - forEach((node).heritageClauses, traverse); - return; default: if (isFunctionLike(node)) { let name = (node).name; if (name && name.kind === SyntaxKind.ComputedPropertyName) { + // Note that we will not include methods/accessors of a class because they would require + // first descending into the class. This is by design. traverse((name).expression); return; } diff --git a/tests/baselines/reference/generatorTypeCheck55.errors.txt b/tests/baselines/reference/generatorTypeCheck55.errors.txt new file mode 100644 index 00000000000..0846d1c28ad --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck55.errors.txt @@ -0,0 +1,9 @@ +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck55.ts(2,19): error TS9003: 'class' expressions are not currently supported. + + +==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck55.ts (1 errors) ==== + function* g() { + var x = class C extends (yield) {}; + ~ +!!! error TS9003: 'class' expressions are not currently supported. + } \ No newline at end of file diff --git a/tests/baselines/reference/generatorTypeCheck55.js b/tests/baselines/reference/generatorTypeCheck55.js new file mode 100644 index 00000000000..dd7cd33af29 --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck55.js @@ -0,0 +1,11 @@ +//// [generatorTypeCheck55.ts] +function* g() { + var x = class C extends (yield) {}; +} + +//// [generatorTypeCheck55.js] +function* g() { + var x = class C extends (yield) { + } + ; +} diff --git a/tests/baselines/reference/generatorTypeCheck56.errors.txt b/tests/baselines/reference/generatorTypeCheck56.errors.txt new file mode 100644 index 00000000000..c5ab01662bd --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck56.errors.txt @@ -0,0 +1,16 @@ +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck56.ts(2,19): error TS9003: 'class' expressions are not currently supported. +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck56.ts(3,11): error TS1163: A 'yield' expression is only allowed in a generator declaration. + + +==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck56.ts (2 errors) ==== + function* g() { + var x = class C { + ~ +!!! error TS9003: 'class' expressions are not currently supported. + *[yield 0]() { + ~~~~~ +!!! error TS1163: A 'yield' expression is only allowed in a generator declaration. + yield 0; + } + }; + } \ No newline at end of file diff --git a/tests/baselines/reference/generatorTypeCheck56.js b/tests/baselines/reference/generatorTypeCheck56.js new file mode 100644 index 00000000000..c1b0f98f5e7 --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck56.js @@ -0,0 +1,18 @@ +//// [generatorTypeCheck56.ts] +function* g() { + var x = class C { + *[yield 0]() { + yield 0; + } + }; +} + +//// [generatorTypeCheck56.js] +function* g() { + var x = class C { + *[yield 0]() { + yield 0; + } + } + ; +} diff --git a/tests/baselines/reference/generatorTypeCheck57.errors.txt b/tests/baselines/reference/generatorTypeCheck57.errors.txt new file mode 100644 index 00000000000..9734f445537 --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck57.errors.txt @@ -0,0 +1,14 @@ +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck57.ts(2,11): error TS9004: 'class' declarations are only supported directly inside a module or as a top level declaration. +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck57.ts(3,13): error TS1163: A 'yield' expression is only allowed in a generator declaration. + + +==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck57.ts (2 errors) ==== + function* g() { + class C { + ~ +!!! error TS9004: 'class' declarations are only supported directly inside a module or as a top level declaration. + x = yield 0; + ~~~~~ +!!! error TS1163: A 'yield' expression is only allowed in a generator declaration. + }; + } \ No newline at end of file diff --git a/tests/baselines/reference/generatorTypeCheck57.js b/tests/baselines/reference/generatorTypeCheck57.js new file mode 100644 index 00000000000..740df492867 --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck57.js @@ -0,0 +1,16 @@ +//// [generatorTypeCheck57.ts] +function* g() { + class C { + x = yield 0; + }; +} + +//// [generatorTypeCheck57.js] +function* g() { + class C { + constructor() { + this.x = yield 0; + } + } + ; +} diff --git a/tests/baselines/reference/generatorTypeCheck58.errors.txt b/tests/baselines/reference/generatorTypeCheck58.errors.txt new file mode 100644 index 00000000000..f091857cec3 --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck58.errors.txt @@ -0,0 +1,14 @@ +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck58.ts(2,11): error TS9004: 'class' declarations are only supported directly inside a module or as a top level declaration. +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck58.ts(3,20): error TS1163: A 'yield' expression is only allowed in a generator declaration. + + +==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck58.ts (2 errors) ==== + function* g() { + class C { + ~ +!!! error TS9004: 'class' declarations are only supported directly inside a module or as a top level declaration. + static x = yield 0; + ~~~~~ +!!! error TS1163: A 'yield' expression is only allowed in a generator declaration. + }; + } \ No newline at end of file diff --git a/tests/baselines/reference/generatorTypeCheck58.js b/tests/baselines/reference/generatorTypeCheck58.js new file mode 100644 index 00000000000..925fc2b76d2 --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck58.js @@ -0,0 +1,14 @@ +//// [generatorTypeCheck58.ts] +function* g() { + class C { + static x = yield 0; + }; +} + +//// [generatorTypeCheck58.js] +function* g() { + class C { + } + C.x = yield 0; + ; +} diff --git a/tests/baselines/reference/generatorTypeCheck59.errors.txt b/tests/baselines/reference/generatorTypeCheck59.errors.txt new file mode 100644 index 00000000000..541fb491af6 --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck59.errors.txt @@ -0,0 +1,15 @@ +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck59.ts(2,11): error TS9004: 'class' declarations are only supported directly inside a module or as a top level declaration. +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck59.ts(3,11): error TS1163: A 'yield' expression is only allowed in a generator declaration. + + +==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck59.ts (2 errors) ==== + function* g() { + class C { + ~ +!!! error TS9004: 'class' declarations are only supported directly inside a module or as a top level declaration. + @(yield "") + ~~~~~ +!!! error TS1163: A 'yield' expression is only allowed in a generator declaration. + m() { } + }; + } \ No newline at end of file diff --git a/tests/baselines/reference/generatorTypeCheck59.js b/tests/baselines/reference/generatorTypeCheck59.js new file mode 100644 index 00000000000..5b3b70386f8 --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck59.js @@ -0,0 +1,27 @@ +//// [generatorTypeCheck59.ts] +function* g() { + class C { + @(yield "") + m() { } + }; +} + +//// [generatorTypeCheck59.js] +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc); + switch (arguments.length) { + case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target); + case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0); + case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc); + } +}; +function* g() { + class C { + m() { } + } + Object.defineProperty(C.prototype, "m", + __decorate([ + (yield "") + ], C.prototype, "m", Object.getOwnPropertyDescriptor(C.prototype, "m"))); + ; +} diff --git a/tests/baselines/reference/generatorTypeCheck60.errors.txt b/tests/baselines/reference/generatorTypeCheck60.errors.txt new file mode 100644 index 00000000000..39724d9ac6d --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck60.errors.txt @@ -0,0 +1,12 @@ +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck60.ts(2,11): error TS9004: 'class' declarations are only supported directly inside a module or as a top level declaration. +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck60.ts(2,21): error TS9002: Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clauses. + + +==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck60.ts (2 errors) ==== + function* g() { + class C extends (yield) {}; + ~ +!!! error TS9004: 'class' declarations are only supported directly inside a module or as a top level declaration. + ~~~~~~~ +!!! error TS9002: Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clauses. + } \ No newline at end of file diff --git a/tests/baselines/reference/generatorTypeCheck60.js b/tests/baselines/reference/generatorTypeCheck60.js new file mode 100644 index 00000000000..cb7c65cf081 --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck60.js @@ -0,0 +1,11 @@ +//// [generatorTypeCheck60.ts] +function* g() { + class C extends (yield) {}; +} + +//// [generatorTypeCheck60.js] +function* g() { + class C extends (yield) { + } + ; +} diff --git a/tests/baselines/reference/generatorTypeCheck61.errors.txt b/tests/baselines/reference/generatorTypeCheck61.errors.txt new file mode 100644 index 00000000000..3c9f3819d61 --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck61.errors.txt @@ -0,0 +1,22 @@ +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck61.ts(2,5): error TS1129: Statement expected. +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck61.ts(2,12): error TS1146: Declaration expected. +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck61.ts(2,13): error TS1005: ')' expected. +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck61.ts(2,14): error TS1005: ';' expected. +tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck61.ts(4,1): error TS1128: Declaration or statement expected. + + +==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck61.ts (5 errors) ==== + function * g() { + @(yield 0) + ~ +!!! error TS1129: Statement expected. + +!!! error TS1146: Declaration expected. + ~ +!!! error TS1005: ')' expected. + ~ +!!! error TS1005: ';' expected. + class C {}; + } + ~ +!!! error TS1128: Declaration or statement expected. \ No newline at end of file diff --git a/tests/baselines/reference/generatorTypeCheck61.js b/tests/baselines/reference/generatorTypeCheck61.js new file mode 100644 index 00000000000..591e52f269e --- /dev/null +++ b/tests/baselines/reference/generatorTypeCheck61.js @@ -0,0 +1,12 @@ +//// [generatorTypeCheck61.ts] +function * g() { + @(yield 0) + class C {}; +} + +//// [generatorTypeCheck61.js] +function* g() { } +0; +class C { +} +; diff --git a/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck55.ts b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck55.ts new file mode 100644 index 00000000000..2041764a2f9 --- /dev/null +++ b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck55.ts @@ -0,0 +1,4 @@ +//@target: ES6 +function* g() { + var x = class C extends (yield) {}; +} \ No newline at end of file diff --git a/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck56.ts b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck56.ts new file mode 100644 index 00000000000..7aa66480a16 --- /dev/null +++ b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck56.ts @@ -0,0 +1,8 @@ +//@target: ES6 +function* g() { + var x = class C { + *[yield 0]() { + yield 0; + } + }; +} \ No newline at end of file diff --git a/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck57.ts b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck57.ts new file mode 100644 index 00000000000..f972a2a60e3 --- /dev/null +++ b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck57.ts @@ -0,0 +1,6 @@ +//@target: ES6 +function* g() { + class C { + x = yield 0; + }; +} \ No newline at end of file diff --git a/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck58.ts b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck58.ts new file mode 100644 index 00000000000..081979be505 --- /dev/null +++ b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck58.ts @@ -0,0 +1,6 @@ +//@target: ES6 +function* g() { + class C { + static x = yield 0; + }; +} \ No newline at end of file diff --git a/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck59.ts b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck59.ts new file mode 100644 index 00000000000..81aacecd7a3 --- /dev/null +++ b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck59.ts @@ -0,0 +1,7 @@ +//@target: ES6 +function* g() { + class C { + @(yield "") + m() { } + }; +} \ No newline at end of file diff --git a/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck60.ts b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck60.ts new file mode 100644 index 00000000000..36747936f17 --- /dev/null +++ b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck60.ts @@ -0,0 +1,4 @@ +//@target: ES6 +function* g() { + class C extends (yield) {}; +} \ No newline at end of file diff --git a/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck61.ts b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck61.ts new file mode 100644 index 00000000000..31c36e2a626 --- /dev/null +++ b/tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck61.ts @@ -0,0 +1,5 @@ +//@target: ES6 +function * g() { + @(yield 0) + class C {}; +} \ No newline at end of file