From 33cf2e5bcad869b408b87ec7924886dacf86a5be Mon Sep 17 00:00:00 2001
From: jumbatm <jumbatm@gmail.com>
Date: Mon, 16 Nov 2020 17:48:41 +1000
Subject: [PATCH 1/3] Document SessionDiagnostic

---
 src/SUMMARY.md                       |  1 +
 src/diagnostics.md                   |  4 ++
 src/diagnostics/sessiondiagnostic.md | 97 ++++++++++++++++++++++++++++
 3 files changed, 102 insertions(+)
 create mode 100644 src/diagnostics/sessiondiagnostic.md

diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 41de14954..a94010eb9 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -127,6 +127,7 @@
     - [Two-phase-borrows](./borrow_check/two_phase_borrows.md)
 - [Parameter Environments](./param_env.md)
 - [Errors and Lints](diagnostics.md)
+    - [Creating Errors With SessionDiagnostic](./diagnostics/sessiondiagnostic.md)
     - [`LintStore`](./diagnostics/lintstore.md)
     - [Diagnostic Codes](./diagnostics/diagnostic-codes.md)
 
diff --git a/src/diagnostics.md b/src/diagnostics.md
index 04745ee98..b2191086e 100644
--- a/src/diagnostics.md
+++ b/src/diagnostics.md
@@ -338,6 +338,10 @@ if let Ok(snippet) = sess.source_map().span_to_snippet(sp) {
 err.emit();
 ```
 
+Alternatively, for less-complex diagnostics, the `SessionDiagnostic` derive
+macro can be used -- see [Creating Errors With SessionDiagnostic](./diagnostics/sessiondiagnostic.md).
+
+
 ## Suggestions
 
 In addition to telling the user exactly _why_ their code is wrong, it's
diff --git a/src/diagnostics/sessiondiagnostic.md b/src/diagnostics/sessiondiagnostic.md
new file mode 100644
index 000000000..b64c62007
--- /dev/null
+++ b/src/diagnostics/sessiondiagnostic.md
@@ -0,0 +1,97 @@
+# Creating Errors With SessionDiagnostic
+
+The SessionDiagnostic derive macro gives an alternate way to the DiagnosticBuilder API for defining
+and emitting errors.  It allows a struct to be annotated with information which allows it to be
+transformed and emitted as a Diagnostic.
+
+As an example, we'll take a look at how the "field already declared" diagnostic is actually defined
+in the compiler (see the definition
+[here](https://github.com/rust-lang/rust/blob/75042566d1c90d912f22e4db43b6d3af98447986/compiler/rustc_typeck/src/errors.rs#L65-L74)
+and usage
+[here](https://github.com/rust-lang/rust/blob/75042566d1c90d912f22e4db43b6d3af98447986/compiler/rustc_typeck/src/collect.rs#L863-L867)):
+
+```rust,ignore
+#[derive(SessionDiagnostic)]
+#[error = "E0124"]
+pub struct FieldAlreadyDeclared {
+    pub field_name: Ident,
+    #[message = "field `{field_name}` is already declared"]
+    #[label = "field already declared"]
+    pub span: Span,
+    #[label = "`{field_name}` first declared here"]
+    pub prev_span: Span,
+}
+// ...
+tcx.sess.emit_err(FieldAlreadyDeclared {
+    field_name: f.ident,
+    span: f.span,
+    prev_span,
+});
+```
+
+We see that using `SessionDiagnostic` is relatively straight forward.  The `#[error = "..."]`
+attribute is used to supply the error code for the diagnostic.  We are then annotate fields in the
+struct with various information of how to convert an instance of the struct into a proper
+diagnostic.  The attributes above produce code which is roughly equivalent to the following (in
+pseudo-Rust):
+
+```rust,ignore
+impl SessionDiagnostic for FieldAlreadyDeclared {
+    fn into_diagnostic(self, sess: &'_ rustc_session::Session) -> DiagnosticBuilder<'_> {
+        let mut diag = sess.struct_err_with_code("", rustc_errors::DiagnosticId::Error("E0124"));
+        diag.set_span(self.span);
+        diag.set_primary_message(format!("field `{field_name}` is already declared", field_name = self.field_name));
+        diag.span_label(self.span, "field already declared");
+        diag.span_label(self.prev_span, format!("`{field_name}` first declared here", field_name = self.field_name));
+        diag
+    }
+}
+```
+
+The generated code draws attention to a number of features.  First, we see that within the strings
+passed to each attribute, we see that field names can be referenced without needing to be passed
+explicitly into the format string -- in this example here, `#[message = "field {field_name} is
+already declared"]` produces a call to `format!` with the appropriate arguments to format
+`self.field_name` into the string.  This applies to strings passed to all attributes.
+
+We also see that labelling `Span` fields in the struct produces calls which pass that `Span` to the
+produced diagnostic.  In the example above, we see that putting the `#[message = "..."]` attribute
+on a `Span` leads to the primary span of the diagnostic being set to that `Span`, while applying the
+`#[label = "..."]` attribute on a Span will simply set the span for that label.
+Each attribute has different requirements for what they can be applied on, differing on position
+(on the struct, or on a specific field), type (if it's applied on a field), and whether or not the
+attribute is optional.
+
+## Attributes Listing
+
+Below is a listing of all the currently-available attributes that `#[derive(SessionDiagnostic)]`
+understands:
+
+Attribute                                               | Applied to                                        | Mandatory | Behaviour
+:--------------                                         | :--------------------                             |:--------- | :---------
+`#[code = "..."]`                                       | Struct                                            | Yes       | Sets the Diagnostic's error code
+`#[message = "..."]`                                    | Struct / `Span` fields                            | Yes       | Sets the Diagnostic's primary message. If on `Span` field, also sets the Diagnostic's span.
+`#[label = "..."]`                                      | `Span` fields                                     | No        | Equivalent to calling `span_label` with that Span and message.
+`#[suggestion(message = "..." , code = "..."]`          | `(Span, MachineApplicability)` or `Span` fields   | No        | Equivalent to calling `span_suggestion`. Note `code` is optional.
+`#[suggestion_short(message = "..." , code = "..."]`    | `(Span, MachineApplicability)` or `Span` fields   | No        | Equivalent to calling `span_suggestion_short`. Note `code` is optional.
+`#[suggestion_hidden(message = "..." , code = "..."]`   | `(Span, MachineApplicability)` or `Span` fields   | No        | Equivalent to calling `span_suggestion_hidden`. Note `code` is optional.
+`#[suggestion_verbose(message = "..." , code = "..."]`  | `(Span, MachineApplicability)` or `Span` fields   | No        | Equivalent to calling `span_suggestion_verbose`. Note `code` is optional.
+
+
+## Optional Diagnostic Attributes
+
+There may be some cases where you want one of the decoration attributes to be applied optionally;
+for example, if a suggestion can only be generated sometimes. In this case, simply wrap the field's
+type in an `Option`.  At runtime, if the field is set to `None`, the attribute for that field won't
+be used in creating the diagnostic. For example:
+
+```rust,ignored
+#[derive(SessionDiagnostic)]
+#[code = "E0123"]
+struct SomeKindOfError {
+    ...
+    #[suggestion(message = "informative error message")]
+    opt_sugg: Option<(Span, Applicability)>
+    ...
+}
+```

From 532d28be8958c1e0f0b7a1cb06caac14b3b82d4c Mon Sep 17 00:00:00 2001
From: jumbatm <jumbatm@gmail.com>
Date: Mon, 16 Nov 2020 17:48:41 +1000
Subject: [PATCH 2/3] Apply suggestions from review.

Co-authored-by: Camelid <camelidcamel@gmail.com>
---
 src/diagnostics/sessiondiagnostic.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/diagnostics/sessiondiagnostic.md b/src/diagnostics/sessiondiagnostic.md
index b64c62007..4583e6d3d 100644
--- a/src/diagnostics/sessiondiagnostic.md
+++ b/src/diagnostics/sessiondiagnostic.md
@@ -30,8 +30,8 @@ tcx.sess.emit_err(FieldAlreadyDeclared {
 ```
 
 We see that using `SessionDiagnostic` is relatively straight forward.  The `#[error = "..."]`
-attribute is used to supply the error code for the diagnostic.  We are then annotate fields in the
-struct with various information of how to convert an instance of the struct into a proper
+attribute is used to supply the error code for the diagnostic.  We then annotate fields in the
+struct with various information on how to convert an instance of the struct into a rendered
 diagnostic.  The attributes above produce code which is roughly equivalent to the following (in
 pseudo-Rust):
 
@@ -49,7 +49,7 @@ impl SessionDiagnostic for FieldAlreadyDeclared {
 ```
 
 The generated code draws attention to a number of features.  First, we see that within the strings
-passed to each attribute, we see that field names can be referenced without needing to be passed
+passed to each attribute, field names can be referenced without needing to be passed
 explicitly into the format string -- in this example here, `#[message = "field {field_name} is
 already declared"]` produces a call to `format!` with the appropriate arguments to format
 `self.field_name` into the string.  This applies to strings passed to all attributes.

From 01be82c1ac7574593b92687d383c13eee30a09b9 Mon Sep 17 00:00:00 2001
From: jumbatm <jumbatm@gmail.com>
Date: Mon, 16 Nov 2020 17:48:41 +1000
Subject: [PATCH 3/3] Use shorter [][]-style link to not pass 100 chars.

---
 src/diagnostics.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/diagnostics.md b/src/diagnostics.md
index b2191086e..978ef3988 100644
--- a/src/diagnostics.md
+++ b/src/diagnostics.md
@@ -339,7 +339,9 @@ err.emit();
 ```
 
 Alternatively, for less-complex diagnostics, the `SessionDiagnostic` derive
-macro can be used -- see [Creating Errors With SessionDiagnostic](./diagnostics/sessiondiagnostic.md).
+macro can be used -- see [Creating Errors With SessionDiagnostic][sessiondiagnostic].
+
+[sessiondiagnostic]: ./diagnostics/sessiondiagnostic.md
 
 
 ## Suggestions