From 7250d2562be881f9cdc24c6646468e0f3afcaf9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 4 Nov 2021 12:12:42 -0400 Subject: [PATCH] Simplifiy tracking of valid JSX positions (#13891) Remove `state.inPropertyName` and simplifies `state.canStartJSXElement` tracking --- .../babel-parser/src/parser/expression.js | 14 ++++---- .../babel-parser/src/plugins/jsx/index.js | 34 +++++-------------- .../src/plugins/typescript/index.js | 2 +- packages/babel-parser/src/tokenizer/index.js | 10 +++++- packages/babel-parser/src/tokenizer/state.js | 3 +- packages/babel-parser/src/tokenizer/types.js | 6 ++++ 6 files changed, 32 insertions(+), 37 deletions(-) diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 1ae4558ce060..689f3795f884 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -22,12 +22,12 @@ import { tokenCanStartExpression, tokenIsAssignment, tokenIsIdentifier, - tokenIsKeyword, tokenIsKeywordOrIdentifier, tokenIsOperator, tokenIsPostfix, tokenIsPrefix, tokenIsRightAssociative, + tokenKeywordOrIdentifierIsKeyword, tokenLabelName, tokenOperatorPrecedence, tt, @@ -2237,8 +2237,6 @@ export default class ExpressionParser extends LValParser { prop.key = this.parseMaybeAssignAllowIn(); this.expect(tt.bracketR); } else { - const oldInPropertyName = this.state.inPropertyName; - this.state.inPropertyName = true; // We check if it's valid for it to be a private name when we push it. const type = this.state.type; (prop: $FlowFixMe).key = @@ -2253,8 +2251,6 @@ export default class ExpressionParser extends LValParser { // ClassPrivateProperty is never computed, so we don't assign in that case. prop.computed = false; } - - this.state.inPropertyName = oldInPropertyName; } return prop.key; @@ -2584,12 +2580,16 @@ export default class ExpressionParser extends LValParser { throw this.unexpected(); } + const tokenIsKeyword = tokenKeywordOrIdentifierIsKeyword(type); + if (liberal) { // If the current token is not used as a keyword, set its type to "tt.name". // This will prevent this.next() from throwing about unexpected escapes. - this.state.type = tt.name; + if (tokenIsKeyword) { + this.replaceToken(tt.name); + } } else { - this.checkReservedWord(name, start, tokenIsKeyword(type), false); + this.checkReservedWord(name, start, tokenIsKeyword, false); } this.next(); diff --git a/packages/babel-parser/src/plugins/jsx/index.js b/packages/babel-parser/src/plugins/jsx/index.js index eabf67a94abe..682ad64ec904 100644 --- a/packages/babel-parser/src/plugins/jsx/index.js +++ b/packages/babel-parser/src/plugins/jsx/index.js @@ -21,10 +21,6 @@ import { isIdentifierChar, isIdentifierStart } from "../../util/identifier"; import type { Position } from "../../util/location"; import { isNewLine } from "../../util/whitespace"; import { Errors, makeErrorTemplates, ErrorCodes } from "../../parser/error"; -import type { LookaheadState } from "../../tokenizer/state"; -import State from "../../tokenizer/state"; - -type JSXLookaheadState = LookaheadState & { inPropertyName: boolean }; const HEX_NUMBER = /^[\da-fA-F]+$/; const DECIMAL_NUMBER = /^\d+$/; @@ -106,7 +102,7 @@ export default (superClass: Class): Class => case charCodes.lessThan: case charCodes.leftCurlyBrace: if (this.state.pos === this.state.start) { - if (ch === charCodes.lessThan && this.state.exprAllowed) { + if (ch === charCodes.lessThan && this.state.canStartJSXElement) { ++this.state.pos; return this.finishToken(tt.jsxTagStart); } @@ -556,24 +552,14 @@ export default (superClass: Class): Class => ) { // In case we encounter an lt token here it will always be the start of // jsx as the lt sign is not allowed in places that expect an expression - this.finishToken(tt.jsxTagStart); + this.replaceToken(tt.jsxTagStart); return this.jsxParseElement(); } else { return super.parseExprAtom(refExpressionErrors); } } - createLookaheadState(state: State): JSXLookaheadState { - const lookaheadState = ((super.createLookaheadState( - state, - ): any): JSXLookaheadState); - lookaheadState.inPropertyName = state.inPropertyName; - return lookaheadState; - } - getTokenFromCode(code: number): void { - if (this.state.inPropertyName) return super.getTokenFromCode(code); - const context = this.curContext(); if (context === tc.j_expr) { @@ -600,7 +586,7 @@ export default (superClass: Class): Class => if ( code === charCodes.lessThan && - this.state.exprAllowed && + this.state.canStartJSXElement && this.input.charCodeAt(this.state.pos + 1) !== charCodes.exclamationMark ) { ++this.state.pos; @@ -617,7 +603,7 @@ export default (superClass: Class): Class => // do not consider JSX expr -> JSX open tag -> ... anymore // reconsider as closing tag context context.splice(-2, 2, tc.j_cTag); - this.state.exprAllowed = false; + this.state.canStartJSXElement = false; } else if (type === tt.jsxTagStart) { context.push( tc.j_expr, // treat as beginning of JSX expression @@ -627,17 +613,13 @@ export default (superClass: Class): Class => const out = context.pop(); if ((out === tc.j_oTag && prevType === tt.slash) || out === tc.j_cTag) { context.pop(); - this.state.exprAllowed = context[context.length - 1] === tc.j_expr; + this.state.canStartJSXElement = + context[context.length - 1] === tc.j_expr; } else { - this.state.exprAllowed = true; + this.state.canStartJSXElement = true; } - } else if ( - tokenIsKeyword(type) && - (prevType === tt.dot || prevType === tt.questionDot) - ) { - this.state.exprAllowed = false; } else { - this.state.exprAllowed = tokenComesBeforeExpression(type); + this.state.canStartJSXElement = tokenComesBeforeExpression(type); } } }; diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 864a3bb351c8..f4f2580a2415 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -2125,7 +2125,7 @@ export default (superClass: Class): Class => // When ! is consumed as a postfix operator (non-null assertion), // disallow JSX tag forming after. e.g. When parsing `p! < n.p!` // ` = [ct.brace]; // Used to track whether a JSX element is allowed to form - exprAllowed: boolean = true; + canStartJSXElement: boolean = true; // Used to signal to callers of `readWord1` whether the word // contained any escape sequences. This is needed because words with diff --git a/packages/babel-parser/src/tokenizer/types.js b/packages/babel-parser/src/tokenizer/types.js index 974d9bcb9591..51539b05e077 100644 --- a/packages/babel-parser/src/tokenizer/types.js +++ b/packages/babel-parser/src/tokenizer/types.js @@ -332,6 +332,12 @@ export function tokenIsIdentifier(token: TokenType): boolean { return token >= tt._as && token <= tt.name; } +export function tokenKeywordOrIdentifierIsKeyword(token: TokenType): boolean { + // we can remove the token >= tt._in check when we + // know a token is either keyword or identifier + return token <= tt._while; +} + export function tokenIsKeywordOrIdentifier(token: TokenType): boolean { return token >= tt._in && token <= tt.name; }