From 9eac2efdc99e1db6993a49301e4df5cb68e348e8 Mon Sep 17 00:00:00 2001 From: Jamund Ferguson Date: Mon, 9 Mar 2015 22:05:26 -0700 Subject: [PATCH] Fix: generator methods in classes (fixes #85) --- espree.js | 80 ++++++--- .../classes-and-generators.config.js | 4 + .../classes-and-generators.result.js | 153 ++++++++++++++++++ .../classes-and-generators.src.js | 4 + .../error-generator.config.js | 4 + .../error-generator.result.js | 6 + .../error-generator.src.js | 4 + .../error-no-class.config.js | 5 + .../error-no-class.result.js | 6 + .../error-no-class.src.js | 4 + .../error-static-no-generators.config.js | 4 + .../error-static-no-generators.result.js | 6 + .../error-static-no-generators.src.js | 4 + .../static-generators.config.js | 4 + .../static-generators.result.js | 153 ++++++++++++++++++ .../static-generators.src.js | 4 + .../error-no-shorthand.config.js | 4 + .../error-no-shorthand.result.js | 6 + .../error-no-shorthand.src.js | 1 + 19 files changed, 434 insertions(+), 22 deletions(-) create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/classes-and-generators.config.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/classes-and-generators.result.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/classes-and-generators.src.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/error-generator.config.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/error-generator.result.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/error-generator.src.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/error-no-class.config.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/error-no-class.result.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/error-no-class.src.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/error-static-no-generators.config.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/error-static-no-generators.result.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/error-static-no-generators.src.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/static-generators.config.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/static-generators.result.js create mode 100644 tests/fixtures/ecma-features-mix/classes-and-generators/static-generators.src.js create mode 100644 tests/fixtures/ecma-features-mix/objectLiteralShorthandMethods-and-generators/error-no-shorthand.config.js create mode 100644 tests/fixtures/ecma-features-mix/objectLiteralShorthandMethods-and-generators/error-no-shorthand.result.js create mode 100644 tests/fixtures/ecma-features-mix/objectLiteralShorthandMethods-and-generators/error-no-shorthand.src.js diff --git a/espree.js b/espree.js index a7395a7a..b60e0551 100644 --- a/espree.js +++ b/espree.js @@ -1957,7 +1957,7 @@ function parseJSXElement() { * Applies location information to the given node by using the given marker. * The marker indicates the point at which the node is said to have to begun * in the source code. - * @param {Object} marker The market to use for the node. + * @param {Object} marker The marker to use for the node. * @param {ASTNode} node The AST node to apply location information to. * @returns {ASTNode} The node that was passed in. * @private @@ -2451,6 +2451,33 @@ function tryParseMethodDefinition(token, key, computed, marker) { return null; } +/** + * Parses Generator Properties + * @param {ASTNode} key The property key (usually an identifier). + * @param {Object} marker The marker to use for the node. + * @returns {ASTNode} The generator property node. + */ +function parseGeneratorProperty(key, marker) { + + var computed = (lookahead.type === Token.Punctuator && lookahead.value === "["); + + if (!match("(")) { + throwUnexpected(lex()); + } + + return markerApply( + marker, + astNodeFactory.createProperty( + "init", + key, + parsePropertyMethodFunction({ generator: true }), + true, + false, + computed + ) + ); +} + // TODO(nzakas): Update to match Esprima function parseObjectProperty() { var token, key, id, computed, methodMarker, options; @@ -2610,32 +2637,17 @@ function parseObjectProperty() { ); } - // only possibility in this branch is a generator + // only possibility in this branch is a shorthand generator if (token.type === Token.EOF || token.type === Token.Punctuator) { - if (!allowGenerators || !match("*")) { + if (!allowGenerators || !match("*") || !allowMethod) { throwUnexpected(token); } - lex(); - computed = (lookahead.type === Token.Punctuator && lookahead.value === "["); + lex(); id = parseObjectPropertyKey(); - if (!match("(")) { - throwUnexpected(lex()); - } - - return markerApply( - marker, - astNodeFactory.createProperty( - "init", - id, - parsePropertyMethodFunction({ generator: true }), - true, - false, - computed - ) - ); + return parseGeneratorProperty(id, marker); } @@ -4908,7 +4920,9 @@ function parseImportDeclaration() { // 14.5 Class Definitions function parseClassBody() { - var token, isStatic, hasConstructor = false, body = [], method, computed, key; + var hasConstructor = false, generator = false, + allowGenerators = extra.ecmaFeatures.generators, + token, isStatic, body = [], method, computed, key; var existingProps = {}, topMarker = markerCreate(), @@ -4929,10 +4943,28 @@ function parseClassBody() { token = lookahead; isStatic = false; + generator = match("*"); computed = match("["); marker = markerCreate(); + + if (generator) { + if (!allowGenerators) { + throwUnexpected(lookahead); + } + lex(); + } + key = parseObjectPropertyKey(); + // static generator methods + if (key.name === "static" && match("*")) { + if (!allowGenerators) { + throwUnexpected(lookahead); + } + generator = true; + lex(); + } + if (key.name === "static" && lookaheadPropertyName()) { token = lookahead; isStatic = true; @@ -4940,7 +4972,11 @@ function parseClassBody() { key = parseObjectPropertyKey(); } - method = tryParseMethodDefinition(token, key, computed, marker); + if (generator) { + method = parseGeneratorProperty(key, marker); + } else { + method = tryParseMethodDefinition(token, key, computed, marker, generator); + } if (method) { method.static = isStatic; diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/classes-and-generators.config.js b/tests/fixtures/ecma-features-mix/classes-and-generators/classes-and-generators.config.js new file mode 100644 index 00000000..65354f7e --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/classes-and-generators.config.js @@ -0,0 +1,4 @@ +module.exports = { + classes: true, + generators: true +}; \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/classes-and-generators.result.js b/tests/fixtures/ecma-features-mix/classes-and-generators/classes-and-generators.result.js new file mode 100644 index 00000000..bd6fdaef --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/classes-and-generators.result.js @@ -0,0 +1,153 @@ +module.exports = { + "type": "Program", + "body": [ + { + "type": "ClassDeclaration", + "id": { + "type": "Identifier", + "name": "Foo", + "range": [ + 6, + 9 + ], + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 9 + } + } + }, + "superClass": null, + "body": { + "type": "ClassBody", + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "bar", + "range": [ + 14, + 17 + ], + "loc": { + "start": { + "line": 2, + "column": 2 + }, + "end": { + "line": 2, + "column": 5 + } + } + }, + "value": { + "type": "FunctionExpression", + "id": null, + "params": [], + "defaults": [], + "body": { + "type": "BlockStatement", + "body": [], + "range": [ + 20, + 24 + ], + "loc": { + "start": { + "line": 2, + "column": 8 + }, + "end": { + "line": 3, + "column": 2 + } + } + }, + "rest": null, + "generator": true, + "expression": false, + "range": [ + 17, + 24 + ], + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 3, + "column": 2 + } + } + }, + "kind": "method", + "computed": false, + "range": [ + 13, + 24 + ], + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 3, + "column": 2 + } + }, + "static": false + } + ], + "range": [ + 10, + 26 + ], + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 4, + "column": 1 + } + } + }, + "range": [ + 0, + 26 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + } + } + ], + "range": [ + 0, + 26 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + } +}; \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/classes-and-generators.src.js b/tests/fixtures/ecma-features-mix/classes-and-generators/classes-and-generators.src.js new file mode 100644 index 00000000..ca3360a7 --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/classes-and-generators.src.js @@ -0,0 +1,4 @@ +class Foo { + *bar() { + } +} \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/error-generator.config.js b/tests/fixtures/ecma-features-mix/classes-and-generators/error-generator.config.js new file mode 100644 index 00000000..93e315c3 --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/error-generator.config.js @@ -0,0 +1,4 @@ +module.exports = { + classes: true, + generators: false +}; \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/error-generator.result.js b/tests/fixtures/ecma-features-mix/classes-and-generators/error-generator.result.js new file mode 100644 index 00000000..ba06fee6 --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/error-generator.result.js @@ -0,0 +1,6 @@ +module.exports = { + "index": 16, + "lineNumber": 2, + "column": 5, + "description": "Unexpected token *" +}; diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/error-generator.src.js b/tests/fixtures/ecma-features-mix/classes-and-generators/error-generator.src.js new file mode 100644 index 00000000..a35a8c89 --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/error-generator.src.js @@ -0,0 +1,4 @@ +class Foo { + *bar() { + } +} \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/error-no-class.config.js b/tests/fixtures/ecma-features-mix/classes-and-generators/error-no-class.config.js new file mode 100644 index 00000000..0fcd9d99 --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/error-no-class.config.js @@ -0,0 +1,5 @@ +module.exports = { + classes: false, + objectLiteralShorthandMethods: false, + generators: false +}; \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/error-no-class.result.js b/tests/fixtures/ecma-features-mix/classes-and-generators/error-no-class.result.js new file mode 100644 index 00000000..796742e7 --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/error-no-class.result.js @@ -0,0 +1,6 @@ +module.exports = { + "index": 0, + "lineNumber": 1, + "column": 1, + "description": "Unexpected reserved word" +}; \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/error-no-class.src.js b/tests/fixtures/ecma-features-mix/classes-and-generators/error-no-class.src.js new file mode 100644 index 00000000..a35a8c89 --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/error-no-class.src.js @@ -0,0 +1,4 @@ +class Foo { + *bar() { + } +} \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/error-static-no-generators.config.js b/tests/fixtures/ecma-features-mix/classes-and-generators/error-static-no-generators.config.js new file mode 100644 index 00000000..93e315c3 --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/error-static-no-generators.config.js @@ -0,0 +1,4 @@ +module.exports = { + classes: true, + generators: false +}; \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/error-static-no-generators.result.js b/tests/fixtures/ecma-features-mix/classes-and-generators/error-static-no-generators.result.js new file mode 100644 index 00000000..5e4e4a0d --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/error-static-no-generators.result.js @@ -0,0 +1,6 @@ +module.exports = { + "index": 20, + "lineNumber": 2, + "column": 9, + "description": "Unexpected token *" +}; \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/error-static-no-generators.src.js b/tests/fixtures/ecma-features-mix/classes-and-generators/error-static-no-generators.src.js new file mode 100644 index 00000000..24da58eb --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/error-static-no-generators.src.js @@ -0,0 +1,4 @@ +class Foo { + static *bar() { + } +} \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/static-generators.config.js b/tests/fixtures/ecma-features-mix/classes-and-generators/static-generators.config.js new file mode 100644 index 00000000..65354f7e --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/static-generators.config.js @@ -0,0 +1,4 @@ +module.exports = { + classes: true, + generators: true +}; \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/static-generators.result.js b/tests/fixtures/ecma-features-mix/classes-and-generators/static-generators.result.js new file mode 100644 index 00000000..98d57ad0 --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/static-generators.result.js @@ -0,0 +1,153 @@ +module.exports = { + "type": "Program", + "body": [ + { + "type": "ClassDeclaration", + "id": { + "type": "Identifier", + "name": "Foo", + "range": [ + 6, + 9 + ], + "loc": { + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 9 + } + } + }, + "superClass": null, + "body": { + "type": "ClassBody", + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "bar", + "range": [ + 21, + 24 + ], + "loc": { + "start": { + "line": 2, + "column": 9 + }, + "end": { + "line": 2, + "column": 12 + } + } + }, + "value": { + "type": "FunctionExpression", + "id": null, + "params": [], + "defaults": [], + "body": { + "type": "BlockStatement", + "body": [], + "range": [ + 27, + 31 + ], + "loc": { + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 3, + "column": 2 + } + } + }, + "rest": null, + "generator": true, + "expression": false, + "range": [ + 24, + 31 + ], + "loc": { + "start": { + "line": 2, + "column": 12 + }, + "end": { + "line": 3, + "column": 2 + } + } + }, + "kind": "method", + "computed": false, + "range": [ + 13, + 31 + ], + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 3, + "column": 2 + } + }, + "static": true + } + ], + "range": [ + 10, + 33 + ], + "loc": { + "start": { + "line": 1, + "column": 10 + }, + "end": { + "line": 4, + "column": 1 + } + } + }, + "range": [ + 0, + 33 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + } + } + ], + "range": [ + 0, + 33 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 4, + "column": 1 + } + } +}; \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/classes-and-generators/static-generators.src.js b/tests/fixtures/ecma-features-mix/classes-and-generators/static-generators.src.js new file mode 100644 index 00000000..24da58eb --- /dev/null +++ b/tests/fixtures/ecma-features-mix/classes-and-generators/static-generators.src.js @@ -0,0 +1,4 @@ +class Foo { + static *bar() { + } +} \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/objectLiteralShorthandMethods-and-generators/error-no-shorthand.config.js b/tests/fixtures/ecma-features-mix/objectLiteralShorthandMethods-and-generators/error-no-shorthand.config.js new file mode 100644 index 00000000..a93e5488 --- /dev/null +++ b/tests/fixtures/ecma-features-mix/objectLiteralShorthandMethods-and-generators/error-no-shorthand.config.js @@ -0,0 +1,4 @@ +module.exports = { + objectLiteralShorthandMethods: false, + generators: true +}; diff --git a/tests/fixtures/ecma-features-mix/objectLiteralShorthandMethods-and-generators/error-no-shorthand.result.js b/tests/fixtures/ecma-features-mix/objectLiteralShorthandMethods-and-generators/error-no-shorthand.result.js new file mode 100644 index 00000000..7ac6e085 --- /dev/null +++ b/tests/fixtures/ecma-features-mix/objectLiteralShorthandMethods-and-generators/error-no-shorthand.result.js @@ -0,0 +1,6 @@ +module.exports = { + "index": 10, + "lineNumber": 1, + "column": 11, + "description": "Unexpected token *" +}; \ No newline at end of file diff --git a/tests/fixtures/ecma-features-mix/objectLiteralShorthandMethods-and-generators/error-no-shorthand.src.js b/tests/fixtures/ecma-features-mix/objectLiteralShorthandMethods-and-generators/error-no-shorthand.src.js new file mode 100644 index 00000000..1ae2612b --- /dev/null +++ b/tests/fixtures/ecma-features-mix/objectLiteralShorthandMethods-and-generators/error-no-shorthand.src.js @@ -0,0 +1 @@ +var x = { *test () { yield *v } }; \ No newline at end of file