From e02058a646ba42e9a084bbdea6d514bd90b72a8e Mon Sep 17 00:00:00 2001 From: Toru Nagashima Date: Mon, 17 Jun 2019 16:04:30 +0900 Subject: [PATCH] New: Recoverable Error Handling (#19) --- .../2019-recoverable-error-handling/README.md | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 designs/2019-recoverable-error-handling/README.md diff --git a/designs/2019-recoverable-error-handling/README.md b/designs/2019-recoverable-error-handling/README.md new file mode 100644 index 00000000..44832a64 --- /dev/null +++ b/designs/2019-recoverable-error-handling/README.md @@ -0,0 +1,183 @@ +- Start Date: 2019-03-29 +- RFC PR: https://github.com/eslint/rfcs/pull/19 +- Authors: Toru Nagashima <[@mysticatea](https://github.com/mysticatea)> + +# Recoverable Error Handling + +## Summary + +ESLint cannot verify source code if the code has a syntax error. However, we can make valid AST even if several kinds of syntax errors existed. For example, conflict of variable names doesn't break AST. This RFC calls such a syntax error as "Recoverable Errors". + +This RFC adds handling of [Recoverable Errors] into ESLint. + +## Motivation + +The goal of this RFC is that improve ESLint experience by reducing "fixing an error makes more errors." + +This feature intends to be used for the syntax errors which don't affect AST shape. For example, name conflicts, type errors, etc. This feature doesn't intend to support invalid AST. + +## Detailed Design + +### § Handling [Recoverable Errors] in ESLint + +- `Linter` class passes `parserOptions.recoverableErrors` option with `true` to `espree` or custom parsers. +- If the object the parser returned has `recoverableErrors` property with an array, or if the error the parser thrown has `recoverableErrors` property with an array, `Linter` class converts the errors to messages. + + Each element of `recoverableErrors` array has the following form. + + ```jsonc + { + "message": "Identifier 'foo' has already been declared", + "line": 1, // 1-based line number. + "column": 10, // 0-based column number. + "endLine": 1, // Optional. 1-based line number. + "endColumn": 13 // Optional. 0-based column number. + } + ``` + + Then `Linter` class converts that to a message: + + ```jsonc + { + "fatal": true, + "ruleId": null, + "severity": 2, + "message": "Identifier 'foo' has already been declared", + "line": 1, // 1-based line number. + "column": 11, // 1-based column number. + "endLine": 1, // Optional. 1-based line number. + "endColumn": 14 // Optional. 1-based column number. + } + ``` + +- Directive comments such as `/*eslint-disable*/` cannot hide the messages of recoverable errors. +- ESLint doesn't run any rules if a recoverable error existed. + +Practically, this is the support for multiple syntax errors. + +#### `verifyOnRecoverableParsingErrors` option + +`verifyOnRecoverableParsingErrors` option is the following three: + +- `--verify-on-recoverable-parsing-errors` CLI option +- `verifyOnRecoverableParsingErrors` in `CLIEngine` constructor option (`boolean`, default is `false`) +- `verifyOnRecoverableParsingErrors` in `Linter#verify()` option (`boolean`, default is `false`) + +
+An aside:
+And #22 coreOptions.verifyOnRecoverableParsingErrors in config files if both RFCs accepted. +
+ +If the `verifyOnRecoverableParsingErrors` option was given, ESLint runs configured rules even if the parser returned recoverable errors. In that case, ESLint additionally controls lint messages to avoid confusion. + +If the parser returned any recoverable errors: + +- the `Linter` disables autofix by making [`disableFixes` option](https://eslint.org/docs/6.0.0/developer-guide/nodejs-api#linterverify) `true` internally as autofix is considered not safe. +- the `Linter` catches exceptions which were thrown from rules and reports the exceptions as regular messages rather than crash in order to provide linting messages as best effort basis. For example, + + ```jsonc + { + "fatal": true, + "ruleId": "a-rule", + "severity": 2, + "message": "'a-rule' failed to lint the code because of parsing error(s).", + "line": 1, + "column": 1, + "endLine": 1, + "endColumn": 1 + } + ``` + + If a rule has an exception and regular messages, the `Linter` drops the regular messages to avoid confusion of wrong messages. + +### § Handling [Recoverable Errors] in Espree + +Acorn, the underlying of `espree`, has `raiseRecoverable(pos, message)` method to customize handling of recoverable errors. + +If `options.recoverableErrors` was `true` then `espree` collects recoverable errors and returns the errors along with AST. Otherwise, `espree` throws syntax errors on recoverable errors as is currently. + +In `acorn@6.1.1`, there are the following recoverable errors: + +- "Comma is not permitted after the rest element" +- "Parenthesized pattern" +- "Redefinition of `__proto__` property" +- "Redefinition of property" +- "Binding XXX in strict mode" +- "Assigning to XXX in strict mode" +- "Argument name clash" +- "Export 'XXX' is not defined" +- "Multiple default clauses" +- "Identifier 'XXX' has already been declared" +- "Escape sequence in keyword XXX" +- "Invalid regular expression: /a regexp/: An error description" + +> https://github.com/acornjs/acorn/search?q=raiseRecoverable + +
+An aside:
+A crazy idea is that we can make the parsing errors which are caused by older ecmaVersion recoverable. The parser parses code with the latest ecmaVersion always, then reports newer syntaxes as recoverable errors with understandable messages such as "async functions are not supported in ES5. Please set '2017' to 'parserOptions.ecmaVersion'." +
+ +### § Handling [Recoverable Errors] in Other Parsers + +This RFC doesn't contain the update of custom parsers. But this section considers if some popular custom parsers can applicate this feature. + +- `babel-eslint`
+ I don't have enough knowledge about `babel-eslint` and recoverable errors. +- `@typescript-eslint/parser`
+ TypeScript parser parses source code loosely, then provides syntax/semantic errors by API along with AST. So currently `@typescript-eslint/parser` manually throws syntax errors if the parse result has syntax/semantic errors. Therefore, it can provide recoverable errors. +- `vue-eslint-parser`
+ It reports [HTML parse errors](https://html.spec.whatwg.org/multipage/parsing.html#parse-errors) and JavaScript syntax errors in `