Skip to content

Commit

Permalink
Make yield a contextual keyword (#9400)
Browse files Browse the repository at this point in the history
  • Loading branch information
danez committed Jan 23, 2019
1 parent 42c5d3f commit 46ba594
Show file tree
Hide file tree
Showing 38 changed files with 767 additions and 125 deletions.
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() {}}
}

0 comments on commit 46ba594

Please sign in to comment.