|
| 1 | + Author: Roberto Aloi <prof3ta(at)gmail(dot)com> |
| 2 | + Status: Draft |
| 3 | + Type: Standards Track |
| 4 | + Created: 11-Nov-2024 |
| 5 | + Erlang-Version: OTP-28 |
| 6 | + Post-History: |
| 7 | +**** |
| 8 | +EEP 74: Erlang Error Index |
| 9 | +---- |
| 10 | + |
| 11 | +Abstract |
| 12 | +======== |
| 13 | + |
| 14 | +The **Erlang Error Index** is a _catalogue_ of errors emitted by |
| 15 | +various tools within the Erlang ecosystem, including - but not limited |
| 16 | +to - the `erlc` Erlang compiler and the `dialyzer` type checker. |
| 17 | + |
| 18 | +The catalogue is not limited to tools shipped with Erlang/OTP, but it |
| 19 | +can include third-party applications such as the [EqWAlizer][] |
| 20 | +type-checker or the [Elvis][] code style reviewer. |
| 21 | + |
| 22 | +Each error in the catalogue is identified by a **unique code** |
| 23 | +and it is accompanied by a description, examples and possible courses |
| 24 | +of action. Error codes are _namespaced_ based on the tool that |
| 25 | +generates them. Unique codes can be associated to a human-readable |
| 26 | +**alias**. |
| 27 | + |
| 28 | +Unique error codes can be leveraged by IDEs and language servers to |
| 29 | +provide better contextual information about errors and make errors |
| 30 | +easier to search and reference. A standardized error index creates a |
| 31 | +common space for the Community to provide extra examples and |
| 32 | +documentation, creating the perfect companion for the Erlang User |
| 33 | +Manual and standard documentation. |
| 34 | + |
| 35 | +Rationale |
| 36 | +========= |
| 37 | + |
| 38 | +The concept of an "Error Index" for a programming language is not a |
| 39 | +novel idea. Error catalogues already exist, for example, in the |
| 40 | +[Rust][] and [Haskell][] Communities. |
| 41 | + |
| 42 | +Producing meaningful error messages can sometimes be challenging for |
| 43 | +developer tools such as compilers and type checkers due to various |
| 44 | +constraints, including limited context and character count. |
| 45 | + |
| 46 | +By associating a **unique code** to each _diagnostic_ (warning or |
| 47 | +error) we relief tools from having to condense a lot of textual |
| 48 | +information into a - sometime cryptic - generic, single |
| 49 | +sentence. Furthermore, as specific wording of errors and warnings is |
| 50 | +improved over time, error codes remain constant, providing a |
| 51 | +search-engine friendly way to index and reference diagnostics. |
| 52 | + |
| 53 | +An good example of this is the _expression updates a literal_ error |
| 54 | +message, introduced in OTP 27. Given the following code: |
| 55 | + |
| 56 | + -define(DEFAULT, #{timeout => 5000}). |
| 57 | + |
| 58 | + updated(Value) -> |
| 59 | + ?DEFAULT#{timeout => Value}. |
| 60 | + |
| 61 | +The compiler emits the following error: |
| 62 | + |
| 63 | + test.erl:8:11: Warning: expression updates a literal |
| 64 | + % 8| ?DEFAULT#{timeout => 1000}. |
| 65 | + % | ^ |
| 66 | + |
| 67 | +The meaning of the error may not be obvious to everyone. Most |
| 68 | +importantly, the compiler provide no information on why the warning is |
| 69 | +raised and what a user could do about it. The user will then have to |
| 70 | +recur to a search engine, a forum or equivalent to proceed. |
| 71 | + |
| 72 | +Conversely, we can associate a unique identifier to the code (say, |
| 73 | +`ERL-1234`): |
| 74 | + |
| 75 | + test.erl:8:11: Warning: expression updates a literal (ERL-1234) |
| 76 | + % 8| ?DEFAULT#{timeout => 1000}. |
| 77 | + % | ^ |
| 78 | + |
| 79 | +The code make it possible to link the error message to an external |
| 80 | +resource (e.g. a wiki page), which contains all the required, |
| 81 | +additional, information about the error that would not be practical to |
| 82 | +present directly to the user. Here is an example of what the entry |
| 83 | +could look like for the above code: |
| 84 | + |
| 85 | +![Erlang Error Index Sample Entry][] |
| 86 | + |
| 87 | +Unique error codes also have the advantage to be better searchable in |
| 88 | +forums and chats, where the exact error message could vary, but the |
| 89 | +error code would be the same. |
| 90 | + |
| 91 | +Finally, error codes can be used by IDEs (e.g. via language servers) |
| 92 | +to match on error codes and provide contextual help. Both the [Erlang |
| 93 | +LS][] and the [ELP][] language server already use "unofficial" error |
| 94 | +codes. |
| 95 | + |
| 96 | +Emitting Diagnostics |
| 97 | +-------------------- |
| 98 | + |
| 99 | +To make it easier for language servers and IDEs, tools producing |
| 100 | +diagnostics should produce diagnostics (errors and warnings) in a |
| 101 | +standardized format. In the case of the compiler, this could be done |
| 102 | +by specifying an extra option (e.g. `--error-format json`). |
| 103 | + |
| 104 | +A possible JSON format, heavily inspired by the [LSP protocol][], is: |
| 105 | + |
| 106 | +```json |
| 107 | +{ |
| 108 | + uri: "file:///git/erlang/project/app/src/file.erl", |
| 109 | + range: { |
| 110 | + start: { |
| 111 | + line: 5, |
| 112 | + character: 23 |
| 113 | + }, |
| 114 | + end: { |
| 115 | + line: 5, |
| 116 | + character: 32 |
| 117 | + } |
| 118 | + }, |
| 119 | + severity: "warning", |
| 120 | + code: "DIA-1234", |
| 121 | + doc_uri: "https://errors.erlang.org/DIA/DIA-1234", |
| 122 | + source: "dialyzer", |
| 123 | + message: "This a descriptive error message from Dialyzer" |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +Where: |
| 128 | + |
| 129 | +* **uri**: The path of the file the diagnostic refers to, expressed using the [RFC 3986][] format |
| 130 | +* **range**: The range at which the message applies, zero-based. The range should be as strict as possible. For example, if warning |
| 131 | +the user that a record is unused, the range of the diagnostic should |
| 132 | +only cover the name of the record and not the entire definition. This |
| 133 | +minimizes the distraction for the user when, for example, rendered as |
| 134 | +a squiggly line, while conveying the same information. |
| 135 | +* **severity**: The diagnostic's severity. Allowed values are `error`, `warning`, `information`, `hint`. |
| 136 | +* **code**: A unique error code identifying the error |
| 137 | +* **doc_uri**: A URI to open with more information about the diagnostic error |
| 138 | +* **source**: A human-readable string describing the source of the diagnostic |
| 139 | +* **message**: A short, textual description of the error. The message should be general enough and make sense in isolation. |
| 140 | + |
| 141 | +Error Code Format |
| 142 | +----------------- |
| 143 | + |
| 144 | +An error code should be composed by two parts: an alphanumeric |
| 145 | +_namespace_ (three letters) and a numeric identifier (four digits), |
| 146 | +divided by a dash (`-`). |
| 147 | + |
| 148 | +A potential set of namespaces could look like the following: |
| 149 | + |
| 150 | +| Namespace | Description | |
| 151 | +|-----------|-----------------------------------------------------------------| |
| 152 | +| ERL | The Erlang compiler and related tools (linter, parser, scanner) | |
| 153 | +| DIA | The Dialyzer type-checker | |
| 154 | +| ELV | The Elvis code-style reviewer | |
| 155 | +| ELP | The Erlang Language Platform | |
| 156 | +| ... | ... | |
| 157 | + |
| 158 | +A set of potential error codes could look like: |
| 159 | + |
| 160 | + ERL-0123 |
| 161 | + DIA-0009 |
| 162 | + ELV-0015 |
| 163 | + ELP-0001 |
| 164 | + |
| 165 | +The exact number of characters/digits for each namespace and code is |
| 166 | +open for discussion, as well as the fact whether components such as |
| 167 | +the parser, the scanner or the `erlint` Erlang linter should have |
| 168 | +their own namespace. |
| 169 | + |
| 170 | +Responsibilities |
| 171 | +---------------- |
| 172 | + |
| 173 | +The Erlang/OTP team would be ultimately responsible for maintaining a |
| 174 | +list of _official_ namespaces. Each tool maintainer would then be |
| 175 | +responsible to allocate specific codes to specific diagnostics. |
| 176 | + |
| 177 | +Processes |
| 178 | +--------- |
| 179 | + |
| 180 | +The error index can be implemented in the format of Markdown pages. The |
| 181 | +approval process for a namespace (or an error code) will follow a |
| 182 | +regular flow using a Pull Request, reviewed and approved by the |
| 183 | +Erlang/OTP team and, potentially, other interested industrial members. |
| 184 | + |
| 185 | +Errors cannot be re-used. If a tool stops emitting an error code, the |
| 186 | +_deprecated_ error code is still documented in the index, together |
| 187 | +with a deprecation notice. This is to avoid re-using a single code for |
| 188 | +multiple purposes. |
| 189 | + |
| 190 | +To limit the administration burden, the section will contain only |
| 191 | +error codes for the tools shipped with Erlang/OTP and the namespaces |
| 192 | +for external tools. Individual error codes for each namespace would be |
| 193 | +managed by the respective owners. |
| 194 | + |
| 195 | +Reference Implementation |
| 196 | +------------------------ |
| 197 | + |
| 198 | +The [ELP website][] contains a proof of concept of what an Erlang |
| 199 | +Error Index could look like. Ideally, such a website would live under |
| 200 | +the `erlang.org` domain, e.g. using the `https://errors.erlang.org/` URL. |
| 201 | + |
| 202 | +The website should use _Markdown_ as the primary mechanism to write |
| 203 | +content and it should be easily extensible by the Community. |
| 204 | + |
| 205 | +Copyright |
| 206 | +========= |
| 207 | + |
| 208 | +This document is placed in the public domain or under the CC0-1.0-Universal |
| 209 | +license, whichever is more permissive. |
| 210 | + |
| 211 | +[EqWAlizer]: https://github.com/whatsapp/eqwalizer |
| 212 | + "The EqWAlizer Type Checker" |
| 213 | + |
| 214 | +[Elvis]: https://github.com/inaka/elvis |
| 215 | + "The Elvis Style Reviewer" |
| 216 | + |
| 217 | +[Rust]: https://doc.rust-lang.org/error_codes/error-index.html |
| 218 | + "The Rust Error Index" |
| 219 | + |
| 220 | +[Haskell]: https://errors.haskell.org |
| 221 | + "The Haskell Error Index" |
| 222 | + |
| 223 | +[Erlang Error Index Sample Entry]: eep-0074-1.png |
| 224 | + "Erlang Error Index Sample Entry" |
| 225 | + |
| 226 | +[Erlang LS]: https://github.com/erlang-ls/erlang_ls/blob/a4a12001e36b26343d1e9d57a0de0526d90480f2/apps/els_lsp/src/els_compiler_diagnostics.erl#L237 |
| 227 | + "Erlang LS using error codes" |
| 228 | + |
| 229 | +[ELP]: https://github.com/WhatsApp/erlang-language-platform/blob/99a426772be274f3739116736bb22d4c98c123c4/erlang_service/src/erlang_service.erl#L608 |
| 230 | + "ELP using error codes" |
| 231 | + |
| 232 | +[ELP Website]: https://whatsapp.github.io/erlang-language-platform/docs/erlang-error-index/ |
| 233 | + "ELP website" |
| 234 | + |
| 235 | +[LSP Protocol]: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnostic |
| 236 | + |
| 237 | +[RFC 3986]: https://datatracker.ietf.org/doc/html/rfc3986 |
| 238 | + |
| 239 | +[EmacsVar]: <> "Local Variables:" |
| 240 | +[EmacsVar]: <> "mode: indented-text" |
| 241 | +[EmacsVar]: <> "indent-tabs-mode: nil" |
| 242 | +[EmacsVar]: <> "sentence-end-double-space: t" |
| 243 | +[EmacsVar]: <> "fill-column: 70" |
| 244 | +[EmacsVar]: <> "coding: utf-8" |
| 245 | +[EmacsVar]: <> "End:" |
| 246 | +[VimVar]: <> " vim: set fileencoding=utf-8 expandtab shiftwidth=4 softtabstop=4: " |
0 commit comments