fix: implement early errors for record and tuple (#11652)
This commit is contained in:
@@ -72,6 +72,8 @@ export const ErrorMessages = Object.freeze({
|
||||
InvalidParenthesizedAssignment: "Invalid parenthesized assignment pattern",
|
||||
InvalidPrivateFieldResolution: "Private name #%0 is not defined",
|
||||
InvalidPropertyBindingPattern: "Binding member expression",
|
||||
InvalidRecordProperty:
|
||||
"Only properties and spread elements are allowed in record definitions",
|
||||
InvalidRestAssignmentPattern: "Invalid rest operator's argument",
|
||||
LabelRedeclaration: "Label '%0' is already declared",
|
||||
LetInLexicalBinding:
|
||||
@@ -126,6 +128,7 @@ export const ErrorMessages = Object.freeze({
|
||||
"Record expressions starting with '{|' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'bar'",
|
||||
RecordExpressionHashIncorrectStartSyntaxType:
|
||||
"Record expressions starting with '#{' are only allowed when the 'syntaxType' option of the 'recordAndTuple' plugin is set to 'hash'",
|
||||
RecordNoProto: "'__proto__' is not allowed in Record expressions",
|
||||
RestTrailingComma: "Unexpected trailing comma after rest element",
|
||||
SloppyFunction:
|
||||
"In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement",
|
||||
@@ -165,7 +168,7 @@ export const ErrorMessages = Object.freeze({
|
||||
"Private names can only be used as the name of a class element (i.e. class C { #p = 42; #m() {} } )\n or a property of member expression (i.e. this.#p).",
|
||||
UnexpectedReservedWord: "Unexpected reserved word '%0'",
|
||||
UnexpectedSuper: "super is only allowed in object methods and classes",
|
||||
UnexpectedToken: "Unexpected token '%'",
|
||||
UnexpectedToken: "Unexpected token '%0'",
|
||||
UnexpectedTokenUnaryExponentiation:
|
||||
"Illegal expression. Wrap left hand side or entire exponentiation in parentheses.",
|
||||
UnsupportedBind: "Binding should be performed on object property.",
|
||||
|
||||
@@ -74,19 +74,22 @@ export default class ExpressionParser extends LValParser {
|
||||
+takeDecorators: (node: N.HasDecorators) => void;
|
||||
*/
|
||||
|
||||
// Check if property __proto__ has been used more than once.
|
||||
// For object literal, check if property __proto__ has been used more than once.
|
||||
// If the expression is a destructuring assignment, then __proto__ may appear
|
||||
// multiple times. Otherwise, __proto__ is a duplicated key.
|
||||
|
||||
checkDuplicatedProto(
|
||||
// For record expression, check if property __proto__ exists
|
||||
|
||||
checkProto(
|
||||
prop: N.ObjectMember | N.SpreadElement,
|
||||
isRecord: boolean,
|
||||
protoRef: { used: boolean },
|
||||
refExpressionErrors: ?ExpressionErrors,
|
||||
): void {
|
||||
if (
|
||||
prop.type === "SpreadElement" ||
|
||||
prop.type === "ObjectMethod" ||
|
||||
prop.computed ||
|
||||
prop.kind ||
|
||||
// $FlowIgnore
|
||||
prop.shorthand
|
||||
) {
|
||||
@@ -95,9 +98,13 @@ export default class ExpressionParser extends LValParser {
|
||||
|
||||
const key = prop.key;
|
||||
// It is either an Identifier or a String/NumericLiteral
|
||||
const name = key.type === "Identifier" ? key.name : String(key.value);
|
||||
const name = key.type === "Identifier" ? key.name : key.value;
|
||||
|
||||
if (name === "__proto__") {
|
||||
if (isRecord) {
|
||||
this.raise(key.start, Errors.RecordNoProto);
|
||||
return;
|
||||
}
|
||||
if (protoRef.used) {
|
||||
if (refExpressionErrors) {
|
||||
// Store the first redefinition's position, otherwise ignore because
|
||||
@@ -1050,7 +1057,7 @@ export default class ExpressionParser extends LValParser {
|
||||
this.next();
|
||||
node.elements = this.parseExprList(
|
||||
close,
|
||||
true,
|
||||
false,
|
||||
refExpressionErrors,
|
||||
node,
|
||||
);
|
||||
@@ -1554,7 +1561,15 @@ export default class ExpressionParser extends LValParser {
|
||||
const prop = this.parseObjectMember(isPattern, refExpressionErrors);
|
||||
if (!isPattern) {
|
||||
// $FlowIgnore RestElement will never be returned if !isPattern
|
||||
this.checkDuplicatedProto(prop, propHash, refExpressionErrors);
|
||||
this.checkProto(prop, isRecord, propHash, refExpressionErrors);
|
||||
}
|
||||
|
||||
if (
|
||||
isRecord &&
|
||||
prop.type !== "ObjectProperty" &&
|
||||
prop.type !== "SpreadElement"
|
||||
) {
|
||||
this.raise(prop.start, Errors.InvalidRecordProperty);
|
||||
}
|
||||
|
||||
// $FlowIgnore
|
||||
@@ -2088,7 +2103,10 @@ export default class ExpressionParser extends LValParser {
|
||||
allowPlaceholder: ?boolean,
|
||||
): ?N.Expression {
|
||||
let elt;
|
||||
if (allowEmpty && this.match(tt.comma)) {
|
||||
if (this.match(tt.comma)) {
|
||||
if (!allowEmpty) {
|
||||
this.raise(this.state.pos, Errors.UnexpectedToken, ",");
|
||||
}
|
||||
elt = null;
|
||||
} else if (this.match(tt.ellipsis)) {
|
||||
const spreadNodeStartPos = this.state.start;
|
||||
|
||||
@@ -143,37 +143,17 @@ export default (superClass: Class<Parser>): Class<Parser> =>
|
||||
}
|
||||
}
|
||||
|
||||
checkDuplicatedProto(
|
||||
checkProto(
|
||||
prop: N.ObjectMember | N.SpreadElement,
|
||||
isRecord: boolean,
|
||||
protoRef: { used: boolean },
|
||||
refExpressionErrors: ?ExpressionErrors,
|
||||
): void {
|
||||
if (
|
||||
prop.type === "SpreadElement" ||
|
||||
prop.computed ||
|
||||
prop.method ||
|
||||
// $FlowIgnore
|
||||
prop.shorthand
|
||||
) {
|
||||
// $FlowIgnore: check prop.method and fallback to super method
|
||||
if (prop.method) {
|
||||
return;
|
||||
}
|
||||
|
||||
const key = prop.key;
|
||||
// It is either an Identifier or a String/NumericLiteral
|
||||
const name = key.type === "Identifier" ? key.name : String(key.value);
|
||||
|
||||
if (name === "__proto__" && prop.kind === "init") {
|
||||
// Store the first redefinition's position
|
||||
if (protoRef.used) {
|
||||
if (refExpressionErrors?.doubleProto === -1) {
|
||||
refExpressionErrors.doubleProto = key.start;
|
||||
} else {
|
||||
this.raise(key.start, Errors.DuplicateProto);
|
||||
}
|
||||
}
|
||||
|
||||
protoRef.used = true;
|
||||
}
|
||||
super.checkProto(prop, isRecord, protoRef, refExpressionErrors);
|
||||
}
|
||||
|
||||
isValidDirective(stmt: N.Statement): boolean {
|
||||
|
||||
Reference in New Issue
Block a user