Skip to content

Commit e8dd334

Browse files
Merge pull request #70 from robertoaloi/erlang-error-index
EEP 74: Erlang Error Index
2 parents 5f3f970 + c4e0325 commit e8dd334

File tree

2 files changed

+246
-0
lines changed

2 files changed

+246
-0
lines changed

eeps/eep-0074-1.png

335 KB
Loading

eeps/eep-0074.md

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
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

Comments
 (0)