Make yield a contextual keyword (#9400)
This commit is contained in:
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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) &&
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)) {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
"yield is a reserved word inside generator functions",
|
||||
"Can not use 'yield' as identifier inside a generator",
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.inAsync && word === "await") {
|
||||
this.raise(
|
||||
startLoc,
|
||||
"Can not use 'await' as identifier inside an async function",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
@@ -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();
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 }),
|
||||
|
||||
@@ -62,7 +62,6 @@ const keywords = new Set([
|
||||
"extends",
|
||||
"export",
|
||||
"import",
|
||||
"yield",
|
||||
"super",
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user