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
Reduce exprAllowed
usage
#13431
Reduce exprAllowed
usage
#13431
Changes from all commits
a12f921
f357e4f
05b9a6d
80a7adc
ba9d7c9
a5d9102
db4fb1c
7374676
d581a94
56c41be
e841c4e
0970138
fc0fce6
f542cf0
66da823
63deead
930a7aa
f2434cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -261,10 +261,6 @@ export default class ExpressionParser extends LValParser { | |
const startLoc = this.state.startLoc; | ||
if (this.isContextual("yield")) { | ||
if (this.prodParam.hasYield) { | ||
// If we have [Yield] production, `yield` will start a YieldExpression thus | ||
// regex is allowed following. Otherwise `yield` is an identifier and regex | ||
// is disallowed in tt.name.updateContext | ||
this.state.exprAllowed = true; | ||
let left = this.parseYield(); | ||
if (afterLeftParse) { | ||
left = afterLeftParse.call(this, left, startPos, startLoc); | ||
|
@@ -988,11 +984,6 @@ export default class ExpressionParser extends LValParser { | |
// AsyncArrowFunction | ||
|
||
parseExprAtom(refExpressionErrors?: ?ExpressionErrors): N.Expression { | ||
// If a division operator appears in an expression position, the | ||
// tokenizer got confused, and we force it to read a regexp instead. | ||
if (this.state.type === tt.slash) this.readRegexp(); | ||
|
||
const canBeArrow = this.state.potentialArrowAt === this.state.start; | ||
let node; | ||
|
||
switch (this.state.type) { | ||
|
@@ -1017,24 +1008,12 @@ export default class ExpressionParser extends LValParser { | |
return this.finishNode(node, "ThisExpression"); | ||
|
||
case tt.name: { | ||
const canBeArrow = this.state.potentialArrowAt === this.state.start; | ||
const containsEsc = this.state.containsEsc; | ||
const id = this.parseIdentifier(); | ||
|
||
if (!containsEsc && id.name === "async" && !this.canInsertSemicolon()) { | ||
if (this.match(tt._function)) { | ||
const last = this.state.context.length - 1; | ||
if (this.state.context[last] !== ct.functionStatement) { | ||
// Since "async" is an identifier and normally identifiers | ||
// can't be followed by expression, the tokenizer assumes | ||
// that "function" starts a statement. | ||
// Fixing it in the tokenizer would mean tracking not only the | ||
// previous token ("async"), but also the one before to know | ||
// its beforeExpr value. | ||
// It's easier and more efficient to adjust the context here. | ||
throw new Error("Internal error"); | ||
} | ||
this.state.context[last] = ct.functionExpression; | ||
|
||
this.next(); | ||
return this.parseFunction( | ||
this.startNodeAtNode(id), | ||
|
@@ -1073,7 +1052,9 @@ export default class ExpressionParser extends LValParser { | |
return this.parseDo(false); | ||
} | ||
|
||
case tt.regexp: { | ||
case tt.slash: | ||
case tt.slashAssign: { | ||
this.readRegexp(); | ||
return this.parseRegExpLiteral(this.state.value); | ||
} | ||
|
||
|
@@ -1097,8 +1078,10 @@ export default class ExpressionParser extends LValParser { | |
case tt._false: | ||
return this.parseBooleanLiteral(false); | ||
|
||
case tt.parenL: | ||
case tt.parenL: { | ||
const canBeArrow = this.state.potentialArrowAt === this.state.start; | ||
return this.parseParenAndDistinguishExpression(canBeArrow); | ||
} | ||
|
||
case tt.bracketBarL: | ||
case tt.bracketHashL: { | ||
|
@@ -1720,11 +1703,6 @@ export default class ExpressionParser extends LValParser { | |
node.properties.push(prop); | ||
} | ||
|
||
// The tokenizer uses `braceIsBlock` to detect whether `{` starts a block statement. | ||
// If `{` is a block statement, `exprAllowed` will be `true`. | ||
// However the tokenizer can not handle edge cases like `0 ? a : { a : 1 } / 2`, here | ||
// we update `exprAllowed` when an object-like is parsed. | ||
this.state.exprAllowed = false; | ||
this.next(); | ||
|
||
this.state.inFSharpPipelineDirectBody = oldInFSharpPipelineDirectBody; | ||
|
@@ -2514,17 +2492,30 @@ export default class ExpressionParser extends LValParser { | |
); | ||
|
||
this.next(); | ||
if ( | ||
this.match(tt.semi) || | ||
(!this.match(tt.star) && !this.state.type.startsExpr) || | ||
this.hasPrecedingLineBreak() | ||
) { | ||
node.delegate = false; | ||
node.argument = null; | ||
} else { | ||
node.delegate = this.eat(tt.star); | ||
node.argument = this.parseMaybeAssign(); | ||
let delegating = false; | ||
let argument = null; | ||
if (!this.hasPrecedingLineBreak()) { | ||
delegating = this.eat(tt.star); | ||
switch (this.state.type) { | ||
case tt.semi: | ||
case tt.eof: | ||
case tt.braceR: | ||
case tt.parenR: | ||
case tt.bracketR: | ||
case tt.braceBarR: | ||
case tt.colon: | ||
case tt.comma: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The original approach checks However, V8 has
None of the productions before in can parsed down to an argument-less YieldExpression. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it's because of this? function* fn() {
a ? yield : 2
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh I meant for |
||
// The above is the complete set of tokens that can | ||
// follow an AssignmentExpression, and none of them | ||
// can start an AssignmentExpression | ||
if (!delegating) break; | ||
/* fallthrough */ | ||
default: | ||
argument = this.parseMaybeAssign(); | ||
} | ||
} | ||
node.delegate = delegating; | ||
node.argument = argument; | ||
return this.finishNode(node, "YieldExpression"); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,29 +45,18 @@ const JsxErrors = makeErrorTemplates( | |
|
||
// Be aware that this file is always executed and not only when the plugin is enabled. | ||
// Therefore this contexts and tokens do always exist. | ||
tc.j_oTag = new TokContext("<tag", false); | ||
tc.j_cTag = new TokContext("</tag", false); | ||
tc.j_expr = new TokContext("<tag>...</tag>", true, true); | ||
tc.j_oTag = new TokContext("<tag"); | ||
tc.j_cTag = new TokContext("</tag"); | ||
tc.j_expr = new TokContext("<tag>...</tag>", true); | ||
|
||
tt.jsxName = new TokenType("jsxName"); | ||
tt.jsxText = new TokenType("jsxText", { beforeExpr: true }); | ||
tt.jsxTagStart = new TokenType("jsxTagStart", { startsExpr: true }); | ||
tt.jsxTagEnd = new TokenType("jsxTagEnd"); | ||
|
||
tt.jsxTagStart.updateContext = function () { | ||
this.state.context.push(tc.j_expr); // treat as beginning of JSX expression | ||
this.state.context.push(tc.j_oTag); // start opening tag context | ||
this.state.exprAllowed = false; | ||
}; | ||
|
||
tt.jsxTagEnd.updateContext = function (prevType) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is merged with |
||
const out = this.state.context.pop(); | ||
if ((out === tc.j_oTag && prevType === tt.slash) || out === tc.j_cTag) { | ||
this.state.context.pop(); | ||
this.state.exprAllowed = this.curContext() === tc.j_expr; | ||
} else { | ||
this.state.exprAllowed = true; | ||
} | ||
tt.jsxTagStart.updateContext = context => { | ||
context.push(tc.j_expr); // treat as beginning of JSX expression | ||
context.push(tc.j_oTag); // start opening tag context | ||
}; | ||
|
||
function isFragment(object: ?N.JSXElement): boolean { | ||
|
@@ -625,22 +614,35 @@ export default (superClass: Class<Parser>): Class<Parser> => | |
} | ||
|
||
updateContext(prevType: TokenType): void { | ||
if (this.match(tt.braceL)) { | ||
const curContext = this.curContext(); | ||
super.updateContext(prevType); | ||
const { context, type } = this.state; | ||
if (type === tt.braceL) { | ||
const curContext = context[context.length - 1]; | ||
if (curContext === tc.j_oTag) { | ||
this.state.context.push(tc.braceExpression); | ||
context.push(tc.brace); | ||
} else if (curContext === tc.j_expr) { | ||
this.state.context.push(tc.templateQuasi); | ||
} else { | ||
super.updateContext(prevType); | ||
context.push(tc.templateQuasi); | ||
} | ||
this.state.exprAllowed = true; | ||
} else if (this.match(tt.slash) && prevType === tt.jsxTagStart) { | ||
this.state.context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore | ||
this.state.context.push(tc.j_cTag); // reconsider as closing tag context | ||
} else if (type === tt.slash && prevType === tt.jsxTagStart) { | ||
context.length -= 2; // do not consider JSX expr -> JSX open tag -> ... anymore | ||
context.push(tc.j_cTag); // reconsider as closing tag context | ||
this.state.exprAllowed = false; | ||
} else if (type === tt.jsxTagEnd) { | ||
const out = context.pop(); | ||
if ((out === tc.j_oTag && prevType === tt.slash) || out === tc.j_cTag) { | ||
context.pop(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are we popping out here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If If The logic here is was |
||
this.state.exprAllowed = context[context.length - 1] === tc.j_expr; | ||
} else { | ||
this.state.exprAllowed = true; | ||
} | ||
} else if ( | ||
type.keyword && | ||
(prevType === tt.dot || prevType === tt.questionDot) | ||
) { | ||
this.state.exprAllowed = false; | ||
} else { | ||
return super.updateContext(prevType); | ||
this.state.exprAllowed = type.beforeExpr; | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious about why
is disallowed, it doesn't seem to be ambiguous 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question. I have no idea why it is disallowed, maybe @bakkot can shred some light here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That decision was before my time, so I can only speculate. I agree it would still be unambiguous without the NLTH restriction. My best guess is that it's future-proofing, to reserve the possibility of introducing
*
as a prefix operator.