Merge pull request #658 from babel/expect

Syntax Error: add message with the plugin that should be enabled
This commit is contained in:
Henry Zhu 2017-08-28 15:44:26 -06:00 committed by GitHub
commit cde42329ac
138 changed files with 203 additions and 90 deletions

View File

@ -5,7 +5,8 @@
"prettier"
],
"rules": {
"prettier/prettier": ["error", { "trailingComma": "all" }]
"prettier/prettier": ["error", { "trailingComma": "all" }],
"no-case-declarations": "error"
},
"env": {
"node": true

View File

@ -433,12 +433,7 @@ export default class ExpressionParser extends LValParser {
noCalls,
);
} else if (this.match(tt.questionDot)) {
if (!this.hasPlugin("optionalChaining")) {
this.raise(
startPos,
"You can only use optional-chaining when the 'optionalChaining' plugin is enabled.",
);
}
this.expectPlugin("optionalChaining");
if (noCalls && this.lookahead().type == tt.parenL) {
state.stop = true;
@ -660,11 +655,11 @@ export default class ExpressionParser extends LValParser {
return this.finishNode(node, "Super");
case tt._import:
if (this.hasPlugin("importMeta") && this.lookahead().type === tt.dot) {
if (this.lookahead().type === tt.dot) {
return this.parseImportMetaProperty();
}
if (!this.hasPlugin("dynamicImport")) this.unexpected();
this.expectPlugin("dynamicImport");
node = this.startNode();
this.next();
@ -681,7 +676,7 @@ export default class ExpressionParser extends LValParser {
case tt._yield:
if (this.state.inGenerator) this.unexpected();
case tt.name:
case tt.name: {
node = this.startNode();
const allowAwait = this.state.value === "await" && this.state.inAsync;
const allowYield = this.shouldAllowYieldIdentifier();
@ -710,27 +705,29 @@ export default class ExpressionParser extends LValParser {
}
return id;
}
case tt._do:
if (this.hasPlugin("doExpressions")) {
const node = this.startNode();
this.next();
const oldInFunction = this.state.inFunction;
const oldLabels = this.state.labels;
this.state.labels = [];
this.state.inFunction = false;
node.body = this.parseBlock(false);
this.state.inFunction = oldInFunction;
this.state.labels = oldLabels;
return this.finishNode(node, "DoExpression");
}
case tt._do: {
this.expectPlugin("doExpressions");
const node = this.startNode();
this.next();
const oldInFunction = this.state.inFunction;
const oldLabels = this.state.labels;
this.state.labels = [];
this.state.inFunction = false;
node.body = this.parseBlock(false);
this.state.inFunction = oldInFunction;
this.state.labels = oldLabels;
return this.finishNode(node, "DoExpression");
}
case tt.regexp:
case tt.regexp: {
const value = this.state.value;
node = this.parseLiteral(value.value, "RegExpLiteral");
node.pattern = value.pattern;
node.flags = value.flags;
return node;
}
case tt.num:
return this.parseLiteral(this.state.value, "NumericLiteral");
@ -784,7 +781,7 @@ export default class ExpressionParser extends LValParser {
case tt.backQuote:
return this.parseTemplate(false);
case tt.doubleColon:
case tt.doubleColon: {
node = this.startNode();
this.next();
node.object = null;
@ -797,6 +794,7 @@ export default class ExpressionParser extends LValParser {
"Binding should be performed on object property.",
);
}
}
default:
throw this.unexpected();
@ -825,11 +823,7 @@ export default class ExpressionParser extends LValParser {
parseFunctionExpression(): N.FunctionExpression | N.MetaProperty {
const node = this.startNode();
const meta = this.parseIdentifier(true);
if (
this.state.inGenerator &&
this.hasPlugin("functionSent") &&
this.eat(tt.dot)
) {
if (this.state.inGenerator && this.eat(tt.dot)) {
return this.parseMetaProperty(node, meta, "sent");
}
return this.parseFunction(node, false);
@ -841,6 +835,16 @@ export default class ExpressionParser extends LValParser {
propertyName: string,
): N.MetaProperty {
node.meta = meta;
if (meta.name === "function" && propertyName === "sent") {
if (this.isContextual(propertyName)) {
this.expectPlugin("functionSent");
} else if (!this.hasPlugin("functionSent")) {
// They didn't actually say `function.sent`, just `function.`, so a simple error would be less confusing.
this.unexpected();
}
}
node.property = this.parseIdentifier(true);
if (node.property.name !== propertyName) {
@ -857,6 +861,18 @@ export default class ExpressionParser extends LValParser {
const node = this.startNode();
const id = this.parseIdentifier(true);
this.expect(tt.dot);
if (id.name === "import") {
if (this.isContextual("meta")) {
this.expectPlugin("importMeta");
} else if (!this.hasPlugin("importMeta")) {
this.raise(
id.start,
`Dynamic imports require a parameter: import('a.js').then`,
);
}
}
if (!this.inModule) {
this.raise(
id.start,
@ -1141,7 +1157,8 @@ export default class ExpressionParser extends LValParser {
decorators = [];
}
if (this.hasPlugin("objectRestSpread") && this.match(tt.ellipsis)) {
if (this.match(tt.ellipsis)) {
this.expectPlugin("objectRestSpread");
prop = this.parseSpread(isPattern ? { start: 0 } : undefined);
if (isPattern) {
this.toAssignable(prop, true, "object pattern");
@ -1199,8 +1216,11 @@ export default class ExpressionParser extends LValParser {
prop.computed = false;
} else {
isAsync = true;
if (this.hasPlugin("asyncGenerators"))
isGenerator = this.eat(tt.star);
if (this.match(tt.star)) {
this.expectPlugin("asyncGenerators");
this.next();
isGenerator = true;
}
this.parsePropertyName(prop);
}
} else {

View File

@ -10,7 +10,11 @@ import CommentsParser from "./comments";
// message.
export default class LocationParser extends CommentsParser {
raise(pos: number, message: string): empty {
raise(
pos: number,
message: string,
missingPluginNames?: Array<string>,
): empty {
const loc = getLineInfo(this.input, pos);
message += ` (${loc.line}:${loc.column})`;
// $FlowIgnore
@ -19,6 +23,9 @@ export default class LocationParser extends CommentsParser {
);
err.pos = pos;
err.loc = loc;
if (missingPluginNames) {
err.missingPlugin = missingPluginNames;
}
throw err;
}
}

View File

@ -69,13 +69,14 @@ export default class LValParser extends NodeUtils {
this.toAssignable(node.value, isBinding, contextDescription);
break;
case "SpreadElement":
case "SpreadElement": {
this.checkToRestConversion(node);
node.type = "RestElement";
const arg = node.argument;
this.toAssignable(arg, isBinding, contextDescription);
break;
}
case "ArrayExpression":
node.type = "ArrayPattern";
@ -211,11 +212,12 @@ export default class LValParser extends NodeUtils {
case tt.name:
return this.parseBindingIdentifier();
case tt.bracketL:
case tt.bracketL: {
const node = this.startNode();
this.next();
node.elements = this.parseBindingList(tt.bracketR, true);
return this.finishNode(node, "ArrayPattern");
}
case tt.braceL:
return this.parseObj(true);

View File

@ -237,9 +237,7 @@ export default class StatementParser extends ExpressionParser {
}
parseDecorator(): N.Decorator {
if (!(this.hasPlugin("decorators") || this.hasPlugin("decorators2"))) {
this.unexpected();
}
this.expectOnePlugin(["decorators", "decorators2"]);
const node = this.startNode();
this.next();
@ -340,11 +338,8 @@ export default class StatementParser extends ExpressionParser {
this.state.labels.push(loopLabel);
let forAwait = false;
if (
this.hasPlugin("asyncGenerators") &&
this.state.inAsync &&
this.isContextual("await")
) {
if (this.state.inAsync && this.isContextual("await")) {
this.expectPlugin("asyncGenerators");
forAwait = true;
this.next();
}
@ -489,12 +484,13 @@ export default class StatementParser extends ExpressionParser {
if (this.match(tt._catch)) {
const clause = this.startNode();
this.next();
if (this.match(tt.parenL) || !this.hasPlugin("optionalCatchBinding")) {
if (this.match(tt.parenL)) {
this.expect(tt.parenL);
clause.param = this.parseBindingAtom();
this.checkLVal(clause.param, true, Object.create(null), "catch clause");
this.expect(tt.parenR);
} else {
this.expectPlugin("optionalCatchBinding");
clause.param = null;
}
clause.body = this.parseBlock();
@ -782,12 +778,11 @@ export default class StatementParser extends ExpressionParser {
this.initFunction(node, isAsync);
if (this.match(tt.star)) {
if (node.async && !this.hasPlugin("asyncGenerators")) {
this.unexpected();
} else {
node.generator = true;
this.next();
if (node.async) {
this.expectPlugin("asyncGenerators");
}
node.generator = true;
this.next();
}
if (
@ -960,8 +955,9 @@ export default class StatementParser extends ExpressionParser {
isStatic = true;
}
if (this.hasPlugin("classPrivateProperties") && this.match(tt.hash)) {
if (this.match(tt.hash)) {
// Private property
this.expectPlugin("classPrivateProperties");
this.next();
const privateProp: N.ClassPrivateProperty = memberAny;
privateProp.key = this.parseIdentifier(true);
@ -1047,8 +1043,12 @@ export default class StatementParser extends ExpressionParser {
this.pushClassProperty(classBody, prop);
} else if (isSimple && key.name === "async" && !this.isLineTerminator()) {
// an async method
const isGenerator =
this.hasPlugin("asyncGenerators") && this.eat(tt.star);
let isGenerator = false;
if (this.match(tt.star)) {
this.expectPlugin("asyncGenerators");
this.next();
isGenerator = true;
}
method.kind = "method";
this.parsePropertyName(method);
if (this.isNonstaticConstructor(method)) {
@ -1151,17 +1151,14 @@ export default class StatementParser extends ExpressionParser {
}
parseClassProperty(node: N.ClassProperty): N.ClassProperty {
const noPluginMsg =
"You can only use Class Properties when the 'classProperties' plugin is enabled.";
if (!node.typeAnnotation && !this.hasPlugin("classProperties")) {
this.raise(node.start, noPluginMsg);
if (!node.typeAnnotation) {
this.expectPlugin("classProperties");
}
this.state.inClassProperty = true;
if (this.match(tt.eq)) {
if (!this.hasPlugin("classProperties"))
this.raise(this.state.start, noPluginMsg);
this.expectPlugin("classProperties");
this.next();
node.value = this.parseMaybeAssign();
} else {
@ -1169,6 +1166,7 @@ export default class StatementParser extends ExpressionParser {
}
this.semicolon();
this.state.inClassProperty = false;
return this.finishNode(node, "ClassProperty");
}

View File

@ -109,4 +109,26 @@ export default class UtilParser extends Tokenizer {
}
throw this.raise(pos != null ? pos : this.state.start, messageOrType);
}
expectPlugin(name: string): void {
if (!this.hasPlugin(name)) {
throw this.raise(
this.state.start,
`This experimental syntax requires enabling the parser plugin: '${name}'`,
[name],
);
}
}
expectOnePlugin(names: Array<string>): void {
if (!names.some(n => this.hasPlugin(n))) {
throw this.raise(
this.state.start,
`This experimental syntax requires enabling one of the following parser plugin(s): '${names.join(
", ",
)}'`,
names,
);
}
}
}

View File

@ -540,7 +540,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
switch (this.state.type) {
case tt.name:
case tt._void:
case tt._null:
case tt._null: {
const type = this.match(tt._void)
? "TSVoidKeyword"
: this.match(tt._null)
@ -552,6 +552,7 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.finishNode(node, type);
}
return this.tsParseTypeReference();
}
case tt.string:
case tt.num:
case tt._true:
@ -573,13 +574,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
return this.finishNode(node, "TSLiteralType");
}
break;
case tt._this:
case tt._this: {
const thisKeyword = this.tsParseThisTypeNode();
if (this.isContextual("is") && !this.hasPrecedingLineBreak()) {
return this.tsParseThisTypePredicate(thisKeyword);
} else {
return thisKeyword;
}
}
case tt._typeof:
return this.tsParseTypeQuery();
case tt.braceL:
@ -1038,13 +1040,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
case tt._var:
case tt._let:
return this.parseVarStatement(nany, this.state.type);
case tt.name:
case tt.name: {
const value = this.state.value;
if (value === "global") {
return this.tsParseAmbientExternalModuleDeclaration(nany);
} else {
return this.tsParseDeclaration(nany, value, /* next */ true);
}
}
}
}
@ -1064,14 +1067,14 @@ export default (superClass: Class<Parser>): Class<Parser> =>
tsParseExpressionStatement(node: any, expr: N.Identifier): ?N.Declaration {
switch (expr.name) {
case "declare":
case "declare": {
const declaration = this.tsTryParseDeclare(node);
if (declaration) {
declaration.declare = true;
return declaration;
}
break;
}
case "global":
// `global { }` (with no `declare`) may appear inside an ambient module declaration.
// Would like to use tsParseAmbientExternalModuleDeclaration here, but already ran past "global".

View File

@ -595,11 +595,13 @@ export default class Tokenizer extends LocationParser {
++this.state.pos;
return this.finishToken(tt.backQuote);
case 48: // '0'
case 48: {
// '0'
const next = this.input.charCodeAt(this.state.pos + 1);
if (next === 120 || next === 88) return this.readRadixNumber(16); // '0x', '0X' - hex number
if (next === 111 || next === 79) return this.readRadixNumber(8); // '0o', '0O' - octal number
if (next === 98 || next === 66) return this.readRadixNumber(2); // '0b', '0B' - binary number
}
// Anything else beginning with a digit is an integer, octal
// number, or float.
case 49:

View File

@ -1,3 +1,3 @@
{
"throws": "You can only use Class Properties when the 'classProperties' plugin is enabled. (2:2)"
"throws": "This experimental syntax requires enabling the parser plugin: 'classProperties' (3:2)"
}

View File

@ -0,0 +1 @@
1n

View File

@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'bigInt' (1:1)",
"plugins": []
}

View File

@ -0,0 +1,4 @@
class Point {
#x = 1;
#y = 2;
}

View File

@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'classPrivateProperties' (2:3)",
"plugins": []
}

View File

@ -0,0 +1 @@
export A from 'test';

View File

@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'exportExtensions' (1:1)",
"plugins": []
}

View File

@ -0,0 +1 @@
1_0

View File

@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'numericSeparator' (1:17)",
"plugins": []
}

View File

@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'asyncGenerators' (1:15)",
"plugins": []
}

View File

@ -0,0 +1,4 @@
{
"plugins": ["flow"],
"throws": "This experimental syntax requires enabling the parser plugin: 'classProperties' (2:14)"
}

View File

@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'classProperties' (2:6)",
"plugins": []
}

View File

@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'classProperties' (2:5)",
"plugins": []
}

View File

@ -0,0 +1,2 @@
@memoize
function() {}

View File

@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling one of the following parser plugin(s): 'decorators, decorators2' (1:0)",
"plugins": []
}

View File

@ -0,0 +1 @@
(do {x})

View File

@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'doExpressions' (1:1)",
"plugins": []
}

View File

@ -0,0 +1 @@
var $ = import("jquery");

View File

@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'dynamicImport' (1:8)",
"plugins": []
}

View File

@ -0,0 +1 @@
const x = import.meta;

View File

@ -0,0 +1,5 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'importMeta' (1:17)",
"sourceType": "module",
"plugins": []
}

View File

@ -0,0 +1 @@
({...x})

View File

@ -0,0 +1,4 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'objectRestSpread' (1:2)",
"plugins": []
}

View File

@ -0,0 +1,3 @@
{
"throws": "This experimental syntax requires enabling the parser plugin: 'optionalChaining' (1:1)"
}

View File

@ -1,3 +1,3 @@
{
"throws": "You can only use Class Properties when the 'classProperties' plugin is enabled. (2:2)"
"throws": "This experimental syntax requires enabling the parser plugin: 'classProperties' (3:2)"
}

View File

@ -1,4 +0,0 @@
{
"plugins": ["flow"],
"throws": "You can only use Class Properties when the 'classProperties' plugin is enabled. (2:14)"
}

View File

@ -1,3 +0,0 @@
{
"throws": "You can only use Class Properties when the 'classProperties' plugin is enabled. (2:2)"
}

View File

@ -1,3 +0,0 @@
{
"throws": "You can only use Class Properties when the 'classProperties' plugin is enabled. (2:2)"
}

View File

@ -1,3 +1,3 @@
{
"throws": "Unexpected token, expected ( (2:15)"
"throws": "Dynamic imports require a parameter: import('a.js').then (2:9)"
}

View File

@ -1,3 +1,3 @@
{
"throws": "Unexpected token, expected ( (2:10)"
"throws": "This experimental syntax requires enabling the parser plugin: 'functionSent' (2:11)"
}

View File

@ -1,3 +1,3 @@
{
"throws": "Unexpected token, expected ( (2:11)"
"throws": "This experimental syntax requires enabling the parser plugin: 'functionSent' (2:12)"
}

View File

@ -1,3 +1,3 @@
{
"throws": "Unexpected token, expected ( (2:17)"
"throws": "This experimental syntax requires enabling the parser plugin: 'functionSent' (2:18)"
}

View File

@ -0,0 +1,3 @@
function* foo() {
if (true) function.;
}

View File

@ -0,0 +1,3 @@
{
"throws": "Unexpected token (2:21)"
}

View File

@ -1,4 +0,0 @@
{
"throws": "Unexpected token (1:15)",
"plugins": []
}

Some files were not shown because too many files have changed in this diff Show More