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
Introduce parser error codes #13033
Introduce parser error codes #13033
Conversation
Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/45001/ |
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit f07223d:
|
// These errors have a different error code than the key | ||
export const SourceTypeModuleErrorMessages: ErrorTemplates = Object.freeze({ | ||
ImportMetaOutsideModule: { | ||
code: "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED", |
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 catch!
AccessorIsGenerator: { | ||
code: "AccessorIsGenerator", | ||
template: "A %0ter cannot be a generator", | ||
}, |
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.
It's just a stylistic preference, but I don't really like that we have to repeat so much boilerplate (duplicating every code, and the code
/template
keys for every message).
Maybe we could do something like this?
export const ErrorMessages = makeErrorTemplates({
AccessorIsGenerator: "A %0ter cannot be a generator",
...
});
export function makeErrorTemplates(messages) {
Object.keys(messages).forEach(code => {
messages[code] = { code, template: messages[code] };
});
}
it's a one-time cost only paid when importing @babel/parser
.
/* eslint sort-keys: "error" */ | ||
|
||
/** | ||
* @module parser/error-message | ||
*/ | ||
|
||
// The Errors key follows https://cs.chromium.org/chromium/src/v8/src/common/message-template.h unless it does not exist | ||
export const ErrorMessages = Object.freeze({ | ||
export const ErrorMessages: ErrorTemplates = makeErrorTemplates({ |
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.
Nit: This is probably not needed anymore, since it's inferred from makeErrorTemplates
's return type.
export const ErrorMessages: ErrorTemplates = makeErrorTemplates({ | |
export const ErrorMessages = makeErrorTemplates({ |
throw this.raise(pos != null ? pos : this.state.start, { | ||
code: "UnexpectedToken", | ||
template: messageOrType, | ||
}); |
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 think here it would be better if the signature was
unexpected(pos: ?number, messageOrType: TokenType | ErrorTemplate)
and if we get an error template we use the correct code rather than UnexpectedToken
.
0a70716
to
874ddd6
Compare
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.
👍
Should this wait for a minor? |
Note that the |
`Unexpected token \`${char}\`. Did you mean \`${htmlEntity}\` or \`{'${char}'}\`?`, | ||
); | ||
this.raise(this.state.pos, { | ||
code: "UnexpectedToken", |
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.
Should we prefix error code with BABEL_PARSER
like we did in BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED
? Should we use SNAKE_CASE instead?
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.
Or maybe we could always set code
to BABEL_PARSER_ERROR
, and add a new property (like errorCode
, or parserError
) using these new codes we are introducing.
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 prefer a prefixed error code but don't have a strong opinion. The Node.js recommends error.code
to be the identifier of the error. Conventionally they use SNAKE_CASE. I don't have strong opinion between camelCase and SNAKE_CASE, as long as we have consensus.
https://nodejs.org/api/errors.html#errors_error_code
I would like to maintain the same stability policy as Node.js: The error.code
thrown from @babel/parser
is considered stable: It will only be changed in a major release, but not for error.message
which we will keep polishing between patches/minors.
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.
Yeah but I wonder how useful these specific error messages are.
Except for a few (and they already have specific codes), I think often I would want do do something like this:
try {
somethingThatCallsBabel();
} catch (e) {
if (e.code === "BABEL_PARSER_ERROR") {
console.warn("The file contains invalid syntax!");
} else if (e.code === "BABEL_PARSER_MISSING_PLUGIN") {
console.warn("You might want to enable a parser plugin!");
} else if (e.code === "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED") {
console.warn("You are parsing a module as a script!");
} else if (e.code === "ENOENT") {
console.warn("You are trying to parse a non-existing file!");
} else {
throw e;
}
}
Imho the specific syntax error is a detail at a deeper leverl.
I would like to maintain the same stability policy as Node.js: The
error.code
thrown from@babel/parser
is considered stable
+1
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.
The new example looks good to me. In that case we may consider place current detailed error code to syntaxError
or reasonCode
.
I think BABEL_PARSER_ERROR
is too general, maybe BABEL_PARSER_INVALID_SYNTAX
or BABEL_PARSER_SYNTAX_ERROR
?
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.
Yeah I like that (I don't have a preference about the exact names).
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 would like to maintain the same stability policy as Node.js: The error.code thrown from @babel/parser is considered stable
👍
To summarize, the interface will look like this?:
{
code: "BABEL_PARSER_SYNTAX_ERROR",
reasonCode: "ArgumentsInClass",
message: "'arguments' is only allowed in functions and class methods"
}
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.
Yes 👍
Dismissing my approval for now, since there will be more changes.
Also, |
9413b1f
to
59f07f7
Compare
Wow this PR is getting huge! 🚀😂 Could you also run |
packages/babel-parser/test/expressions/is-expression-babel-parser/fail/8/output.json
Outdated
Show resolved
Hide resolved
9828d62
to
2a737a1
Compare
Maybe we could add a new test file (
|
@@ -73,7 +110,7 @@ export default class ParserError extends CommentsParser { | |||
} | |||
} | |||
} | |||
return this._raise({ loc, pos }, message); | |||
return this._raise({ code, loc, pos }, message); |
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.
It'd be good to add params
as a property.
Use case: in Prettier, Unexpected reserved word 'yield'
should be suppressed, Unexpected reserved word 'for'
should be rethrown. prettier/prettier#10603
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'd prefer to split the error between UnexpectedKeyword
and UnexpectedContextualKeyword
if that works too, and avoid exposing too much internal details about how we build errors.
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.
Yes, that's a better solution for that use case.
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.
@thorn0 I was going to implement this, but I noticed that we already have two different errors:
UnexpectedKeyword
for things likefor
,if
,while
, ...UnexpectedReservedWord
for things likeyield
,await
,enum
, ...
2a737a1
to
f07223d
Compare
I added simple tests! f07223d |
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.
Thanks!
I'll merge this to a feature branch (since we don't merge "New feature" PRs until we are ready for a minor version), so that you or someone else can work on #13033 (comment) on top of this one.
If you have an idea for better interface, please comment!