Make sure babel parser throws exactly same recoverable errors when estree plugin is enabled (#12375)
* refactor: introduce isPrivateName and getPrivateNameSV * feat: check recoverable errors on estree-throw * fix: pass through all params of parseBlockBody * fix: set bigInt to null when invalid bigInt value is parsed e.g. 0.1n * fix: use string literal value in error message When estree plugin is enabled, stringLiteral#extra.raw is not accessible. Use StringLiteral#value instead. * refactor: introduce hasPropertyAsPrivateName * fix: adapt to ChainExpression * fix: port checkLVal early return for method in object pattern * fix: throw new a?.() on estree * fix: early return for __proto__ in accessors * fix: test record element via isObjectProperty * fix: pass through isLHS in toAssignable * refactor: introduce isObjectMethod methods
This commit is contained in:
@@ -51,7 +51,7 @@ export const ErrorMessages = Object.freeze({
|
||||
ElementAfterRest: "Rest element must be last element",
|
||||
EscapedCharNotAnIdentifier: "Invalid Unicode escape",
|
||||
ExportBindingIsString:
|
||||
"A string literal cannot be used as an exported binding without `from`.\n- Did you mean `export { %0 as '%1' } from 'some-module'`?",
|
||||
"A string literal cannot be used as an exported binding without `from`.\n- Did you mean `export { '%0' as '%1' } from 'some-module'`?",
|
||||
ExportDefaultFromAsIdentifier:
|
||||
"'from' is not allowed as an identifier after 'export default'",
|
||||
ForInOfLoopInitializer:
|
||||
|
||||
@@ -94,8 +94,9 @@ export default class ExpressionParser extends LValParser {
|
||||
): void {
|
||||
if (
|
||||
prop.type === "SpreadElement" ||
|
||||
prop.type === "ObjectMethod" ||
|
||||
this.isObjectMethod(prop) ||
|
||||
prop.computed ||
|
||||
// $FlowIgnore
|
||||
prop.shorthand
|
||||
) {
|
||||
return;
|
||||
@@ -515,11 +516,7 @@ export default class ExpressionParser extends LValParser {
|
||||
|
||||
if (arg.type === "Identifier") {
|
||||
this.raise(node.start, Errors.StrictDelete);
|
||||
} else if (
|
||||
(arg.type === "MemberExpression" ||
|
||||
arg.type === "OptionalMemberExpression") &&
|
||||
arg.property.type === "PrivateName"
|
||||
) {
|
||||
} else if (this.hasPropertyAsPrivateName(arg)) {
|
||||
this.raise(node.start, Errors.DeletePrivateField);
|
||||
}
|
||||
}
|
||||
@@ -618,12 +615,12 @@ export default class ExpressionParser extends LValParser {
|
||||
|
||||
let optional = false;
|
||||
if (this.match(tt.questionDot)) {
|
||||
state.optionalChainMember = optional = true;
|
||||
if (noCalls && this.lookaheadCharCode() === charCodes.leftParenthesis) {
|
||||
// stop at `?.` when parsing `new a?.()`
|
||||
state.stop = true;
|
||||
return base;
|
||||
}
|
||||
state.optionalChainMember = optional = true;
|
||||
this.next();
|
||||
}
|
||||
|
||||
@@ -662,11 +659,14 @@ export default class ExpressionParser extends LValParser {
|
||||
? this.parseExpression()
|
||||
: this.parseMaybePrivateName(true);
|
||||
|
||||
if (property.type === "PrivateName") {
|
||||
if (this.isPrivateName(property)) {
|
||||
if (node.object.type === "Super") {
|
||||
this.raise(startPos, Errors.SuperPrivateField);
|
||||
}
|
||||
this.classScope.usePrivateName(property.id.name, property.start);
|
||||
this.classScope.usePrivateName(
|
||||
this.getPrivateNameSV(property),
|
||||
property.start,
|
||||
);
|
||||
}
|
||||
node.property = property;
|
||||
|
||||
@@ -1496,13 +1496,9 @@ export default class ExpressionParser extends LValParser {
|
||||
// https://tc39.es/ecma262/#prod-NewExpression
|
||||
parseNew(node: N.Expression): N.NewExpression {
|
||||
node.callee = this.parseNoCallExpr();
|
||||
|
||||
if (node.callee.type === "Import") {
|
||||
this.raise(node.callee.start, Errors.ImportCallNotNewExpression);
|
||||
} else if (
|
||||
node.callee.type === "OptionalMemberExpression" ||
|
||||
node.callee.type === "OptionalCallExpression"
|
||||
) {
|
||||
} else if (this.isOptionalChain(node.callee)) {
|
||||
this.raise(this.state.lastTokEnd, Errors.OptionalChainingNoNew);
|
||||
} else if (this.eat(tt.questionDot)) {
|
||||
this.raise(this.state.start, Errors.OptionalChainingNoNew);
|
||||
@@ -1604,7 +1600,7 @@ export default class ExpressionParser extends LValParser {
|
||||
|
||||
if (
|
||||
isRecord &&
|
||||
prop.type !== "ObjectProperty" &&
|
||||
!this.isObjectProperty(prop) &&
|
||||
prop.type !== "SpreadElement"
|
||||
) {
|
||||
this.raise(prop.start, Errors.InvalidRecordProperty);
|
||||
@@ -1923,7 +1919,7 @@ export default class ExpressionParser extends LValParser {
|
||||
? this.parseExprAtom()
|
||||
: this.parseMaybePrivateName(isPrivateNameAllowed);
|
||||
|
||||
if (prop.key.type !== "PrivateName") {
|
||||
if (!this.isPrivateName(prop.key)) {
|
||||
// ClassPrivateProperty is never computed, so we don't assign in that case.
|
||||
prop.computed = false;
|
||||
}
|
||||
|
||||
@@ -445,11 +445,11 @@ export default class LValParser extends NodeUtils {
|
||||
|
||||
case "ObjectPattern":
|
||||
for (let prop of expr.properties) {
|
||||
if (prop.type === "ObjectProperty") prop = prop.value;
|
||||
if (this.isObjectProperty(prop)) prop = prop.value;
|
||||
// If we find here an ObjectMethod, it's because this was originally
|
||||
// an ObjectExpression which has then been converted.
|
||||
// toAssignable already reported this error with a nicer message.
|
||||
else if (prop.type === "ObjectMethod") continue;
|
||||
else if (this.isObjectMethod(prop)) continue;
|
||||
|
||||
this.checkLVal(
|
||||
prop,
|
||||
|
||||
@@ -1346,7 +1346,7 @@ export default class StatementParser extends ExpressionParser {
|
||||
method.kind = "method";
|
||||
this.parseClassElementName(method);
|
||||
|
||||
if (method.key.type === "PrivateName") {
|
||||
if (this.isPrivateName(method.key)) {
|
||||
// Private generator method
|
||||
this.pushClassPrivateMethod(classBody, privateMethod, true, false);
|
||||
return;
|
||||
@@ -1370,7 +1370,7 @@ export default class StatementParser extends ExpressionParser {
|
||||
|
||||
const containsEsc = this.state.containsEsc;
|
||||
const key = this.parseClassElementName(member);
|
||||
const isPrivate = key.type === "PrivateName";
|
||||
const isPrivate = this.isPrivateName(key);
|
||||
// Check the key is not a computed expression or string literal.
|
||||
const isSimple = key.type === "Identifier";
|
||||
const maybeQuestionTokenStart = this.state.start;
|
||||
@@ -1431,7 +1431,7 @@ export default class StatementParser extends ExpressionParser {
|
||||
this.parseClassElementName(method);
|
||||
this.parsePostMemberNameModifiers(publicMember);
|
||||
|
||||
if (method.key.type === "PrivateName") {
|
||||
if (this.isPrivateName(method.key)) {
|
||||
// private async method
|
||||
this.pushClassPrivateMethod(
|
||||
classBody,
|
||||
@@ -1465,7 +1465,7 @@ export default class StatementParser extends ExpressionParser {
|
||||
// The so-called parsed name would have been "get/set": get the real name.
|
||||
this.parseClassElementName(publicMethod);
|
||||
|
||||
if (method.key.type === "PrivateName") {
|
||||
if (this.isPrivateName(method.key)) {
|
||||
// private getter/setter
|
||||
this.pushClassPrivateMethod(classBody, privateMethod, false, false);
|
||||
} else {
|
||||
@@ -1508,7 +1508,10 @@ export default class StatementParser extends ExpressionParser {
|
||||
this.raise(key.start, Errors.StaticPrototype);
|
||||
}
|
||||
|
||||
if (key.type === "PrivateName" && key.id.name === "constructor") {
|
||||
if (
|
||||
this.isPrivateName(key) &&
|
||||
this.getPrivateNameSV(key) === "constructor"
|
||||
) {
|
||||
this.raise(key.start, Errors.ConstructorClassPrivateField);
|
||||
}
|
||||
|
||||
@@ -1571,7 +1574,7 @@ export default class StatementParser extends ExpressionParser {
|
||||
classBody.body.push(node);
|
||||
|
||||
this.classScope.declarePrivateName(
|
||||
node.key.id.name,
|
||||
this.getPrivateNameSV(node.key),
|
||||
CLASS_ELEMENT_OTHER,
|
||||
node.key.start,
|
||||
);
|
||||
@@ -1627,7 +1630,11 @@ export default class StatementParser extends ExpressionParser {
|
||||
? CLASS_ELEMENT_STATIC_SETTER
|
||||
: CLASS_ELEMENT_INSTANCE_SETTER
|
||||
: CLASS_ELEMENT_OTHER;
|
||||
this.classScope.declarePrivateName(node.key.id.name, kind, node.key.start);
|
||||
this.classScope.declarePrivateName(
|
||||
this.getPrivateNameSV(node.key),
|
||||
kind,
|
||||
node.key.start,
|
||||
);
|
||||
}
|
||||
|
||||
// Overridden in typescript.js
|
||||
@@ -1980,7 +1987,7 @@ export default class StatementParser extends ExpressionParser {
|
||||
this.raise(
|
||||
specifier.start,
|
||||
Errors.ExportBindingIsString,
|
||||
local.extra.raw,
|
||||
local.value,
|
||||
exportedName,
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -252,6 +252,51 @@ export default class UtilParser extends Tokenizer {
|
||||
this.match(tt.decimal)
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test if given node is a PrivateName
|
||||
* will be overridden in ESTree plugin
|
||||
*/
|
||||
isPrivateName(node: Node): boolean {
|
||||
return node.type === "PrivateName";
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the string value of a given private name
|
||||
* WITHOUT `#`
|
||||
* @see {@link https://tc39.es/proposal-class-fields/#sec-private-names-static-semantics-stringvalue}
|
||||
*/
|
||||
getPrivateNameSV(node: Node): string {
|
||||
return node.id.name;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return whether the given node is a member/optional chain that
|
||||
* contains a private name as its property
|
||||
* It is overridden in ESTree plugin
|
||||
*/
|
||||
hasPropertyAsPrivateName(node: Node): boolean {
|
||||
return (
|
||||
(node.type === "MemberExpression" ||
|
||||
node.type === "OptionalMemberExpression") &&
|
||||
this.isPrivateName(node.property)
|
||||
);
|
||||
}
|
||||
|
||||
isOptionalChain(node: Node): boolean {
|
||||
return (
|
||||
node.type === "OptionalMemberExpression" ||
|
||||
node.type === "OptionalCallExpression"
|
||||
);
|
||||
}
|
||||
|
||||
isObjectProperty(node: Node): boolean {
|
||||
return node.type === "ObjectProperty";
|
||||
}
|
||||
|
||||
isObjectMethod(node: Node): boolean {
|
||||
return node.type === "ObjectMethod";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user