Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make yield a contextual keyword #9400

Merged
merged 1 commit into from
Jan 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
124 changes: 62 additions & 62 deletions packages/babel-parser/src/parser/expression.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,18 @@ export default class ExpressionParser extends LValParser {
): N.Expression {
const startPos = this.state.start;
const startLoc = this.state.startLoc;
if (this.match(tt._yield) && this.state.inGenerator) {
let left = this.parseYield(noIn);
if (afterLeftParse) {
left = afterLeftParse.call(this, left, startPos, startLoc);
if (this.isContextual("yield")) {
if (this.state.inGenerator) {
let left = this.parseYield(noIn);
if (afterLeftParse) {
left = afterLeftParse.call(this, left, startPos, startLoc);
}
return left;
} else {
// The tokenizer will assume an expression is allowed after
// `yield`, but this isn't that kind of yield
this.state.exprAllowed = false;
}
return left;
}

const oldCommaAfterSpreadAt = this.state.commaAfterSpreadAt;
Expand All @@ -145,7 +151,7 @@ export default class ExpressionParser extends LValParser {
failOnShorthandAssign = true;
}

if (this.match(tt.parenL) || this.match(tt.name) || this.match(tt._yield)) {
if (this.match(tt.parenL) || this.match(tt.name)) {
this.state.potentialArrowAt = this.state.start;
}

Expand Down Expand Up @@ -412,7 +418,13 @@ export default class ExpressionParser extends LValParser {
// Parse unary operators, both prefix and postfix.

parseMaybeUnary(refShorthandDefaultPos: ?Pos): N.Expression {
if (this.state.type.prefix) {
if (
this.isContextual("await") &&
(this.state.inAsync ||
(!this.state.inFunction && this.options.allowAwaitOutsideFunction))
) {
return this.parseAwait();
} else if (this.state.type.prefix) {
const node = this.startNode();
const update = this.match(tt.incDec);
node.operator = this.state.value;
Expand Down Expand Up @@ -841,29 +853,12 @@ export default class ExpressionParser extends LValParser {
this.next();
return this.finishNode(node, "ThisExpression");

case tt._yield:
if (this.state.inGenerator) this.unexpected();

case tt.name: {
node = this.startNode();
const allowAwait =
this.state.value === "await" &&
(this.state.inAsync ||
(!this.state.inFunction && this.options.allowAwaitOutsideFunction));

const containsEsc = this.state.containsEsc;
const allowYield = this.shouldAllowYieldIdentifier();
const id = this.parseIdentifier(allowAwait || allowYield);
const id = this.parseIdentifier();

if (id.name === "await") {
if (
this.state.inAsync ||
this.inModule ||
(!this.state.inFunction && this.options.allowAwaitOutsideFunction)
) {
return this.parseAwait(node);
}
} else if (
if (
!containsEsc &&
id.name === "async" &&
this.match(tt._function) &&
Expand Down Expand Up @@ -1832,17 +1827,14 @@ export default class ExpressionParser extends LValParser {
if (isExpression) {
node.body = this.parseMaybeAssign();
} else {
// Start a new scope with regard to labels and the `inGenerator`
// Start a new scope with regard to labels
// flag (restore them to their old value afterwards).
const oldInGen = this.state.inGenerator;
const oldInFunc = this.state.inFunction;
const oldLabels = this.state.labels;
this.state.inGenerator = node.generator;
this.state.inFunction = true;
this.state.labels = [];
node.body = this.parseBlock(true);
this.state.inFunction = oldInFunc;
this.state.inGenerator = oldInGen;
this.state.labels = oldLabels;
}

Expand Down Expand Up @@ -1952,15 +1944,6 @@ export default class ExpressionParser extends LValParser {
}

parseIdentifierName(pos: number, liberal?: boolean): string {
if (!liberal) {
this.checkReservedWord(
this.state.value,
this.state.start,
!!this.state.type.keyword,
false,
);
}

let name: string;

if (this.match(tt.name)) {
Expand All @@ -1985,11 +1968,17 @@ export default class ExpressionParser extends LValParser {
throw this.unexpected();
}

if (!liberal && name === "await" && this.state.inAsync) {
this.raise(pos, "invalid use of await inside of an async function");
if (!liberal) {
this.checkReservedWord(
name,
this.state.start,
!!this.state.type.keyword,
false,
);
}

this.next();

return name;
}

Expand All @@ -1999,18 +1988,17 @@ export default class ExpressionParser extends LValParser {
checkKeywords: boolean,
isBinding: boolean,
): void {
if (
this.state.strict &&
(isStrictReservedWord(word) ||
(isBinding && isStrictBindReservedWord(word)))
) {
this.raise(startLoc, word + " is a reserved word in strict mode");
if (this.state.inGenerator && word === "yield") {
this.raise(
startLoc,
"Can not use 'yield' as identifier inside a generator",
);
}

if (this.state.inGenerator && word === "yield") {
if (this.state.inAsync && word === "await") {
this.raise(
startLoc,
"yield is a reserved word inside generator functions",
"Can not use 'await' as identifier inside an async function",
);
}

Expand All @@ -2022,20 +2010,40 @@ export default class ExpressionParser extends LValParser {
}

if (this.isReservedWord(word) || (checkKeywords && isKeyword(word))) {
if (!this.state.inAsync && word === "await") {
this.raise(
startLoc,
"Can not use keyword 'await' outside an async function",
);
}
this.raise(startLoc, word + " is a reserved word");
}

if (
this.state.strict &&
(isStrictReservedWord(word) ||
(isBinding && isStrictBindReservedWord(word)))
) {
this.raise(startLoc, word + " is a reserved word in strict mode");
}
}

// Parses await expression inside async function.

parseAwait(node: N.AwaitExpression): N.AwaitExpression {
// istanbul ignore next: this condition is checked at the call site so won't be hit here
parseAwait(): N.AwaitExpression {
const node = this.startNode();

if (
!this.state.inAsync &&
(this.state.inFunction || !this.options.allowAwaitOutsideFunction)
this.state.maybeInArrowParameters &&
// We only set yieldOrAwaitInPossibleArrowParameters if we haven't already
// found a possible invalid AwaitExpression.
!this.state.yieldOrAwaitInPossibleArrowParameters
) {
this.unexpected();
this.state.yieldOrAwaitInPossibleArrowParameters = node;
}

this.next();

if (this.state.inParameters) {
this.raise(
node.start,
Expand All @@ -2048,14 +2056,6 @@ export default class ExpressionParser extends LValParser {
"await* has been removed from the async functions proposal. Use Promise.all() instead.",
);
}
if (
this.state.maybeInArrowParameters &&
// We only set yieldOrAwaitInPossibleArrowParameters if we haven't already
// found a possible invalid AwaitExpression.
!this.state.yieldOrAwaitInPossibleArrowParameters
) {
this.state.yieldOrAwaitInPossibleArrowParameters = node;
}

node.argument = this.parseMaybeUnary();
return this.finishNode(node, "AwaitExpression");
Expand Down
34 changes: 15 additions & 19 deletions packages/babel-parser/src/parser/lval.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,14 @@ import type {
SpreadElement,
} from "../types";
import type { Pos, Position } from "../util/location";
import {
isStrictReservedWord,
isStrictBindReservedWord,
} from "../util/identifier";
import { NodeUtils } from "./node";

export default class LValParser extends NodeUtils {
// Forward-declaration: defined in expression.js
+checkReservedWord: (
word: string,
startLoc: number,
checkKeywords: boolean,
isBinding: boolean,
) => void;
+parseIdentifier: (liberal?: boolean) => Identifier;
+parseMaybeAssign: (
noIn?: ?boolean,
Expand Down Expand Up @@ -224,22 +222,11 @@ export default class LValParser extends NodeUtils {
return this.finishNode(node, "RestElement");
}

shouldAllowYieldIdentifier(): boolean {
return (
this.match(tt._yield) && !this.state.strict && !this.state.inGenerator
);
}

parseBindingIdentifier(): Identifier {
return this.parseIdentifier(this.shouldAllowYieldIdentifier());
}

// Parses lvalue (assignable) atom.
parseBindingAtom(): Pattern {
switch (this.state.type) {
case tt._yield:
case tt.name:
return this.parseBindingIdentifier();
return this.parseIdentifier();

case tt.bracketL: {
const node = this.startNode();
Expand Down Expand Up @@ -347,7 +334,16 @@ export default class LValParser extends NodeUtils {
): void {
switch (expr.type) {
case "Identifier":
this.checkReservedWord(expr.name, expr.start, false, true);
if (
this.state.strict &&
(isStrictReservedWord(expr.name) ||
isStrictBindReservedWord(expr.name))
) {
this.raise(
expr.start,
expr.name + " is a reserved word in strict mode",
);
}

if (checkClashes) {
// we need to prefix this with an underscore for the cases where we have a key of
Expand Down
16 changes: 4 additions & 12 deletions packages/babel-parser/src/parser/statement.js
Original file line number Diff line number Diff line change
Expand Up @@ -975,17 +975,9 @@ export default class StatementParser extends ExpressionParser {

this.initFunction(node, isAsync);

if (this.match(tt.star)) {
node.generator = true;
this.next();
}
node.generator = this.eat(tt.star);

if (
isStatement &&
!optionalId &&
!this.match(tt.name) &&
!this.match(tt._yield)
) {
if (isStatement && !optionalId && !this.match(tt.name)) {
this.unexpected();
}

Expand All @@ -1002,8 +994,8 @@ export default class StatementParser extends ExpressionParser {
this.state.inAsync = isAsync;
this.state.inGenerator = node.generator;
}
if (this.match(tt.name) || this.match(tt._yield)) {
node.id = this.parseBindingIdentifier();
if (this.match(tt.name)) {
node.id = this.parseIdentifier();
}
if (isStatement) {
this.state.inAsync = isAsync;
Expand Down
1 change: 0 additions & 1 deletion packages/babel-parser/src/tokenizer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1360,7 +1360,6 @@ export default class Tokenizer extends LocationParser {
// `tt.name`.
if (
prevType === tt._return ||
prevType === tt._yield ||
(prevType === tt.name && this.state.exprAllowed)
) {
return lineBreak.test(
Expand Down
1 change: 0 additions & 1 deletion packages/babel-parser/src/tokenizer/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ export const keywords = Object.create(null, {
extends: makeKeywordProps("extends", { beforeExpr }),
export: makeKeywordProps("export"),
import: makeKeywordProps("import", { startsExpr }),
yield: makeKeywordProps("yield", { beforeExpr, startsExpr }),
null: makeKeywordProps("null", { startsExpr }),
true: makeKeywordProps("true", { startsExpr }),
false: makeKeywordProps("false", { startsExpr }),
Expand Down
1 change: 0 additions & 1 deletion packages/babel-parser/src/util/identifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ const keywords = new Set([
"extends",
"export",
"import",
"yield",
"super",
]);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"sourceType": "module",
"throws": "await is a reserved word (1:0)"
"throws": "Can not use keyword 'await' outside an async function (1:0)"
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"sourceType": "module",
"throws": "await is a reserved word (1:6)"
"throws": "Can not use keyword 'await' outside an async function (1:6)"
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"sourceType": "module",
"throws": "await is a reserved word (1:8)"
"throws": "Can not use keyword 'await' outside an async function (1:8)"
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"sourceType": "module",
"throws": "await is a reserved word (1:15)"
"throws": "Can not use keyword 'await' outside an async function (1:15)"
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"sourceType": "module",
"throws": "await is a reserved word (1:9)"
"throws": "Can not use keyword 'await' outside an async function (1:9)"
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"sourceType": "module",
"throws": "await is a reserved word (1:6)"
"throws": "Can not use keyword 'await' outside an async function (1:6)"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function* wrap() {
class A {*yield() {}}
}