Skip to content
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

new chapter with examples of diagnostic translation PRs #1621

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@
- [Error codes](./diagnostics/error-codes.md)
- [Diagnostic items](./diagnostics/diagnostic-items.md)
- [`ErrorGuaranteed`](./diagnostics/error-guaranteed.md)
- [Making diagnostics translatable](diagnostics/making-diagnostics-translatable.md)

# MIR to Binaries

Expand Down
195 changes: 195 additions & 0 deletions src/diagnostics/making-diagnostics-translatable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# Making diagnostics translatable

<!-- toc -->

There is an ongoing effort to make diagnostics translatable,
and the coordination happens [on GitHub].

This is intended to be a gentle guide to help with this effort,
with the use of examples.

> A more detailed explanation can be found in [the announcement of the initiative].
> However,
> note that some of the important details are now outdated.
>
> Reference documentation can be found in these chapters:
> - [Diagnostic structs](diagnostic-structs.md)
> - [Translation system](translation.md)

## General comments

For a single diagnostic, 3 files need to be modified:

- One file where the diagnostic is emitted,
typically by calling [Session::struct_span_err].
- One file where the type representing the migrated diagnostic,
either a `struct` or an `enum`, will be added.
This is typically in a module named `errors` in the relevant compiler crate.
- One file where the actual text of the diagnostic is located,
located in a file named `messages.ftl`,
at the root of the relevant rustc crate.

## Simple example

*This uses [issue #108727] for demonstration*

Suppose you have the following code:

```rust
ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit();
```

> Note that `ecx.struct_span_err` indirectly calls [Session::struct_span_err].

Referring to the [general comments](#general-comments) section above,
follow these changes:

- Replace the code above with this:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Replace the code above with this:
- Replace the calls to `struct_span_err` and `emit` with:


```rust
ecx.sess.emit_err(errors::ProcMacroDeriveTokens { span });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should point to emit_err in rustdocs. Also, useful to point out that if the user wants to make a diagnostic, but not emit it, they can use create_err.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tough balance between adding useful info and avoiding the overwhelm... I should find a way to fit create_err in the reference

```

- Create the type introduced above, `errors::ProcMacroDeriveTokens`,
in `src/errors.rs` (relative to crate directory root):

```rust
#[derive(Diagnostic)]
#[diag(expand_proc_macro_derive_tokens)]
pub struct ProcMacroDeriveTokens {
#[primary_span]
Comment on lines +57 to +60
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it may be useful to motivate the three important parts of this:

  • derive(Diagnostic)
  • diag(FLUENT_SLUG)
  • and primary_span

Maybe why we need these three components, what other components are common, etc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I more wanted to have people see the pattern, than do wall-of-text which can overwhelm. The more complete details are in the reference chapters (earlier in the guide).

pub span: Span,
}
```

- Create the actual text of the diagnostic in `compiler/rustc_expand/messages.ftl`:

```fluent
expand_proc_macro_derive_tokens =
proc-macro derive produced unparseable tokens
```

Once that is done, a PR may be submitted,
and the following comment on the PR page will label the PR accordingly,
as well as alert the right people to review it:

```
@rustbot label +A-translation
r? rust-lang/diagnostics
```

## Example with variable interpolation

*This uses [issue #108436] for demonstration*

Suppose you have the following code:

```rust
let mut err = ecx.struct_span_err(span, "proc macro panicked");
if let Some(s) = e.as_str() {
err.help(&format!("message: {}", s));
}
err.emit()
```

> Note that `ecx.struct_span_err` indirectly calls [Session::struct_span_err].

- Replace the code above with this:

```rust
ecx.sess.emit_err(errors::ProcMacroPanicked {
span,
message: e
.as_str()
.map(|message| errors::ProcMacroPanickedHelp { message: message.into() }),
})
```

- Create the type introduced above, `errors::ProcMacroPanickedHelp`,
in `src/errors.rs` (relative to crate directory root):

```rust
#[derive(Diagnostic)]
#[diag(expand_proc_macro_panicked)]
pub(crate) struct ProcMacroPanicked {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub message: Option<ProcMacroPanickedHelp>,
}

#[derive(Subdiagnostic)]
#[help(expand_help)]
pub(crate) struct ProcMacroPanickedHelp {
pub message: String,
}
compiler-errors marked this conversation as resolved.
Show resolved Hide resolved
```

- Create the actual text of the diagnostic in `messages.ftl`
(also at crate directory root):

```fluent
expand_proc_macro_panicked =
proc macro panicked
.help = message: {$message}
```

## Example with a macro, `struct_span_err!`

*This uses [issue #108373] for demonstration*

Suppose you have the following code:

```rust
let mut diag = struct_span_err!(
tcx.sess,
generics_where_clauses_span.unwrap_or(main_span),
E0646,
"`main` function is not allowed to have a `where` clause"
);
if let Some(generics_where_clauses_span) = generics_where_clauses_span {
diag.span_label(generics_where_clauses_span, "`main` cannot have a `where` clause");
}
diag.emit();
```

> Note that `struct_span_err!` indirectly calls [Session::struct_span_err_with_code].

- Replace the code above with this:

```rust
tcx.sess.emit_err(errors::WhereClauseOnMain {
span: generics_where_clauses_span.unwrap_or(main_span),
generics_span: generics_where_clauses_span,
});
```

- Create the type introduced above, `errors::WhereClauseOnMain`,
in `src/errors.rs` (relative to crate directory root):

```rust
#[derive(Diagnostic)]
#[diag(hir_analysis_where_clause_on_main, code = "E0646")]
pub(crate) struct WhereClauseOnMain {
#[primary_span]
pub span: Span,
#[label]
pub generics_span: Option<Span>,
}
```

- Create the actual text of the diagnostic in `messages.ftl`:

```fluent
hir_analysis_where_clause_on_main =
`main` function is not allowed to have a `where` clause
.label = `main` cannot have a `where` clause
```

[Session::struct_span_err]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_session/session/struct.Session.html#method.struct_span_err
[Session::struct_span_err_with_code]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_session/session/struct.Session.html#method.struct_span_err_with_code
[the announcement of the initiative]: https://blog.rust-lang.org/inside-rust/2022/08/16/diagnostic-effort.html#manually-implementing-sessiondiagnostic
[on GitHub]: https://github.com/rust-lang/rust/issues/100717
[issue #108373]: https://github.com/rust-lang/rust/pull/108373
[issue #108436]: https://github.com/rust-lang/rust/pull/108436
[issue #108727]: https://github.com/rust-lang/rust/pull/108727