Disallow yield expressions inside a class

This commit is contained in:
Jason Freeman 2015-05-11 17:40:38 -07:00
parent 670ad05eec
commit cb198aa7f2
24 changed files with 287 additions and 11 deletions

View file

@ -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 === (<FunctionLikeDeclaration>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);
}

View file

@ -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);
}

View file

@ -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(<YieldExpression>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((<ClassDeclaration>node).heritageClauses, traverse);
return;
default:
if (isFunctionLike(node)) {
let name = (<FunctionLikeDeclaration>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((<ComputedPropertyName>name).expression);
return;
}

View file

@ -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.
}

View file

@ -0,0 +1,11 @@
//// [generatorTypeCheck55.ts]
function* g() {
var x = class C extends (yield) {};
}
//// [generatorTypeCheck55.js]
function* g() {
var x = class C extends (yield) {
}
;
}

View file

@ -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;
}
};
}

View file

@ -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;
}
}
;
}

View file

@ -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.
};
}

View file

@ -0,0 +1,16 @@
//// [generatorTypeCheck57.ts]
function* g() {
class C {
x = yield 0;
};
}
//// [generatorTypeCheck57.js]
function* g() {
class C {
constructor() {
this.x = yield 0;
}
}
;
}

View file

@ -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.
};
}

View file

@ -0,0 +1,14 @@
//// [generatorTypeCheck58.ts]
function* g() {
class C {
static x = yield 0;
};
}
//// [generatorTypeCheck58.js]
function* g() {
class C {
}
C.x = yield 0;
;
}

View file

@ -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() { }
};
}

View file

@ -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")));
;
}

View file

@ -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.
}

View file

@ -0,0 +1,11 @@
//// [generatorTypeCheck60.ts]
function* g() {
class C extends (yield) {};
}
//// [generatorTypeCheck60.js]
function* g() {
class C extends (yield) {
}
;
}

View file

@ -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.

View file

@ -0,0 +1,12 @@
//// [generatorTypeCheck61.ts]
function * g() {
@(yield 0)
class C {};
}
//// [generatorTypeCheck61.js]
function* g() { }
0;
class C {
}
;

View file

@ -0,0 +1,4 @@
//@target: ES6
function* g() {
var x = class C extends (yield) {};
}

View file

@ -0,0 +1,8 @@
//@target: ES6
function* g() {
var x = class C {
*[yield 0]() {
yield 0;
}
};
}

View file

@ -0,0 +1,6 @@
//@target: ES6
function* g() {
class C {
x = yield 0;
};
}

View file

@ -0,0 +1,6 @@
//@target: ES6
function* g() {
class C {
static x = yield 0;
};
}

View file

@ -0,0 +1,7 @@
//@target: ES6
function* g() {
class C {
@(yield "")
m() { }
};
}

View file

@ -0,0 +1,4 @@
//@target: ES6
function* g() {
class C extends (yield) {};
}

View file

@ -0,0 +1,5 @@
//@target: ES6
function * g() {
@(yield 0)
class C {};
}