diagnostics: line wrapping/heading changes

Minor stylistic changes to some of the diagnostic documentation: adding
line wrapping to the Markdown source and changing the capitalization of
the headings to be consistent with other pages.

Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
David Wood 2022-06-06 11:04:47 +01:00 committed by Tshepang Mbambo
parent de46205199
commit 2273fee776
4 changed files with 138 additions and 134 deletions

View File

@ -1,4 +1,4 @@
# Diagnostic Codes # Diagnostic codes
We generally try to assign each error message a unique code like `E0123`. These We generally try to assign each error message a unique code like `E0123`. These
codes are defined in the compiler in the `diagnostics.rs` files found in each codes are defined in the compiler in the `diagnostics.rs` files found in each
crate, which basically consist of macros. The codes come in two varieties: those crate, which basically consist of macros. The codes come in two varieties: those
@ -60,7 +60,7 @@ To actually issue the error, you can use the `struct_span_err!` macro:
struct_span_err!(self.tcx.sess, // some path to the session here struct_span_err!(self.tcx.sess, // some path to the session here
span, // whatever span in the source you want span, // whatever span in the source you want
E0592, // your new error code E0592, // your new error code
&format!("text of the error")) fluent::example::an_error_message)
.emit() // actually issue the error .emit() // actually issue the error
``` ```
@ -69,8 +69,8 @@ call `.emit()`:
```rust ```rust
struct_span_err!(...) struct_span_err!(...)
.span_label(another_span, "something to label in the source") .span_label(another_span, fluent::example::example_label)
.span_note(another_span, "some separate note, probably avoid these") .span_note(another_span, fluent::example::separate_note)
.emit_() .emit_()
``` ```

View File

@ -1,19 +1,17 @@
# Diagnostic Items # Diagnostic Items
While writing lints it's common to check for specific types, traits and
## Background functions. This raises the question on how to check for these. Types can be
checked by their complete type path. However, this requires hard coding paths
While writing lints it's common to check for specific types, traits and functions. This raises and can lead to misclassifications in some edge cases. To counteract this,
the question on how to check for these. Types can be checked by their complete type path. rustc has introduced diagnostic items that are used to identify types via
However, this requires hard coding paths and can lead to misclassifications in some edge cases.
To counteract this, rustc has introduced diagnostic items that are used to identify types via
[`Symbol`]s. [`Symbol`]s.
## How To Find Diagnostic Items ## Finding diagnostic items
Diagnostic items are added to items inside `rustc`/`std`/`core` with the
Diagnostic items are added to items inside `rustc`/`std`/`core` with the `rustc_diagnostic_item` `rustc_diagnostic_item` attribute. The item for a specific type can be found by
attribute. The item for a specific type can be found by opening the source code in the opening the source code in the documentation and looking for this attribute.
documentation and looking for this attribute. Note that it's often added with the `cfg_attr` Note that it's often added with the `cfg_attr` attribute to avoid compilation
attribute to avoid compilation errors during tests. A definition often looks like this: errors during tests. A definition often looks like this:
```rs ```rs
// This is the diagnostic item for this type vvvvvvv // This is the diagnostic item for this type vvvvvvv
@ -21,19 +19,20 @@ attribute to avoid compilation errors during tests. A definition often looks lik
struct Penguin; struct Penguin;
``` ```
Diagnostic items are usually only added to traits, types and standalone functions. If the goal Diagnostic items are usually only added to traits, types and standalone
is to check for an associated type or method, please use the diagnostic item of the item and functions. If the goal is to check for an associated type or method, please use
reference [*How To Use Diagnostic Items*](#how-to-use-diagnostic-items). the diagnostic item of the item and reference [*How To Use Diagnostic
Items*](#how-to-use-diagnostic-items).
## How To Add Diagnostic Items
## Adding diagnostic items
A new diagnostic item can be added with these two steps: A new diagnostic item can be added with these two steps:
1. Find the target item inside the Rust repo. Now add the diagnostic item as a string via the 1. Find the target item inside the Rust repo. Now add the diagnostic item as a
`rustc_diagnostic_item` attribute. This can sometimes cause compilation errors while running string via the `rustc_diagnostic_item` attribute. This can sometimes cause
tests. These errors can be avoided by using the `cfg_attr` attribute with the `not(test)` compilation errors while running tests. These errors can be avoided by using
condition (it's fine adding then for all `rustc_diagnostic_item` attributes as a preventive the `cfg_attr` attribute with the `not(test)` condition (it's fine adding
manner). At the end, it should look like this: then for all `rustc_diagnostic_item` attributes as a preventive manner). At
the end, it should look like this:
```rs ```rs
// This will be the new diagnostic item vvv // This will be the new diagnostic item vvv
@ -44,41 +43,45 @@ A new diagnostic item can be added with these two steps:
For the naming conventions of diagnostic items, please refer to For the naming conventions of diagnostic items, please refer to
[*Naming Conventions*](#naming-conventions). [*Naming Conventions*](#naming-conventions).
2. As of <!-- date: 2022-02 --> February 2022, diagnostic items in code are accessed via symbols in 2. As of <!-- date: 2022-02 --> February 2022, diagnostic items in code are
[`rustc_span::symbol::sym`]. To add your newly created diagnostic item simply open the accessed via symbols in [`rustc_span::symbol::sym`]. To add your newly
module file and add the name (In this case `Cat`) at the correct point in the list. created diagnostic item simply open the module file and add the name (In
this case `Cat`) at the correct point in the list.
Now you can create a pull request with your changes. :tada: (Note that when using diagnostic Now you can create a pull request with your changes. :tada: (Note that when
items in other projects like Clippy, it might take some time until the repos get synchronized.) using diagnostic items in other projects like Clippy, it might take some time
until the repos get synchronized.)
## Naming Conventions ## Naming conventions
Diagnostic items don't have a set in stone naming convention yet. These are
some guidelines that should be used for the future, but might differ from
existing names:
Diagnostic items don't have a set in stone naming convention yet. These are some guidelines that * Types, traits and enums are named using UpperCamelCase (Examples: `Iterator`,
should be used for the future, but might differ from existing names: * `HashMap`, ...)
* For type names that are used multiple times like `Writer` it's good to choose
* Types, traits and enums are named using UpperCamelCase (Examples: `Iterator`, `HashMap`, ...) a more precise name, maybe by adding the module to it. (Example: `IoWriter`)
* For type names that are used multiple times like `Writer` it's good to choose a more precise * Associated items should not get their own diagnostic items, but instead be
name, maybe by adding the module to it. (Example: `IoWriter`) accessed indirectly by the diagnostic item of the type they're originating
* Associated items should not get their own diagnostic items, but instead be accessed indirectly from.
by the diagnostic item of the type they're originating from. * Freestanding functions like `std::mem::swap()` should be named using
* Freestanding functions like `std::mem::swap()` should be named using `snake_case` with one `snake_case` with one important (export) module as a prefix (Example:
important (export) module as a prefix (Example: `mem_swap`, `cmp_max`) `mem_swap`, `cmp_max`)
* Modules should usually not have a diagnostic item attached to them. Diagnostic items were * Modules should usually not have a diagnostic item attached to them.
added to avoid the usage of paths, using them on modules would therefore most likely to be Diagnostic items were added to avoid the usage of paths, using them on
counterproductive. modules would therefore most likely to be counterproductive.
## How To Use Diagnostic Items
## Using diagnostic items
In rustc, diagnostic items are looked up via [`Symbol`]s from inside the In rustc, diagnostic items are looked up via [`Symbol`]s from inside the
[`rustc_span::symbol::sym`] module. These can then be mapped to [`DefId`]s using [`rustc_span::symbol::sym`] module. These can then be mapped to [`DefId`]s
[`TyCtxt::get_diagnostic_item()`] or checked if they match a [`DefId`] using using [`TyCtxt::get_diagnostic_item()`] or checked if they match a [`DefId`]
[`TyCtxt::is_diagnostic_item()`]. When mapping from a diagnostic item to a [`DefId`] the method using [`TyCtxt::is_diagnostic_item()`]. When mapping from a diagnostic item to
will return a `Option<DefId>`. This can be `None` if either the symbol isn't a diagnostic item a [`DefId`] the method will return a `Option<DefId>`. This can be `None` if
or the type is not registered, for instance when compiling with `#[no_std]`. All following either the symbol isn't a diagnostic item or the type is not registered, for
examples are based on [`DefId`]s and their usage. instance when compiling with `#[no_std]`. All following examples are based on
[`DefId`]s and their usage.
### Check For A Type
### Example: Checking for a type
```rust ```rust
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
@ -92,8 +95,7 @@ fn example_1(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
} }
``` ```
### Check For A Trait Implementation ### Example: Checking for a trait implementation
```rust ```rust
/// This example checks if a given [`DefId`] from a method is part of a trait /// This example checks if a given [`DefId`] from a method is part of a trait
/// implementation defined by a diagnostic item. /// implementation defined by a diagnostic item.
@ -110,22 +112,21 @@ fn is_diag_trait_item(
``` ```
### Associated Types ### Associated Types
Associated types of diagnostic items can be accessed indirectly by first
Associated types of diagnostic items can be accessed indirectly by first getting the [`DefId`] getting the [`DefId`] of the trait and then calling
of the trait and then calling [`TyCtxt::associated_items()`]. This returns an [`AssocItems`] [`TyCtxt::associated_items()`]. This returns an [`AssocItems`] object which can
object which can be used for further checks. Checkout be used for further checks. Checkout
[`clippy_utils::ty::get_iterator_item_ty()`] for an example usage of this. [`clippy_utils::ty::get_iterator_item_ty()`] for an example usage of this.
### Usage In Clippy ### Usage in Clippy
Clippy tries to use diagnostic items where possible and has developed some
wrapper and utility functions. Please also refer to its documentation when
using diagnostic items in Clippy. (See [*Common tools for writing
lints*][clippy-Common-tools-for-writing-lints].)
Clippy tries to use diagnostic items where possible and has developed some wrapper and utility ## Related issues
functions. Please also refer to its documentation when using diagnostic items in Clippy. (See This lists some related issues. These are probably only interesting to people
[*Common tools for writing lints*][clippy-Common-tools-for-writing-lints].) who really want to take a deep dive into the topic :)
## Related Issues
This lists some related issues. These are probably only interesting to people who really want to
take a deep dive into the topic :)
* [rust#60966]: The Rust PR that introduced diagnostic items * [rust#60966]: The Rust PR that introduced diagnostic items
* [rust-clippy#5393]: Clippy's tracking issue for moving away from hard coded paths to * [rust-clippy#5393]: Clippy's tracking issue for moving away from hard coded paths to

View File

@ -1,5 +1,4 @@
# `ErrorGuaranteed` # `ErrorGuaranteed`
The previous sections have been about the error message that a user of the The previous sections have been about the error message that a user of the
compiler sees. But emitting an error can also have a second important side compiler sees. But emitting an error can also have a second important side
effect within the compiler source code: it generates an effect within the compiler source code: it generates an
@ -19,7 +18,6 @@ There are some important considerations about the usage of `ErrorGuaranteed`:
Thus, you should not rely on Thus, you should not rely on
`ErrorGuaranteed` when deciding whether to emit an error, or what kind of error `ErrorGuaranteed` when deciding whether to emit an error, or what kind of error
to emit. to emit.
* `ErrorGuaranteed` should not be used to indicate that a compilation _will * `ErrorGuaranteed` should not be used to indicate that a compilation _will
emit_ an error in the future. It should be used to indicate that an error emit_ an error in the future. It should be used to indicate that an error
_has already been_ emitted -- that is, the [`emit()`][emit] function has _has already been_ emitted -- that is, the [`emit()`][emit] function has
@ -30,7 +28,6 @@ There are some important considerations about the usage of `ErrorGuaranteed`:
Thankfully, in most cases, it should be statically impossible to abuse Thankfully, in most cases, it should be statically impossible to abuse
`ErrorGuaranteed`. `ErrorGuaranteed`.
[errorguar]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.ErrorGuaranteed.html [errorguar]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.ErrorGuaranteed.html
[rerrors]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/index.html [rerrors]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/index.html
[dsp]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Handler.html#method.delay_span_bug [dsp]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Handler.html#method.delay_span_bug

View File

@ -1,104 +1,110 @@
# Lints # Lints
This page documents some of the machinery around lint registration and how we This page documents some of the machinery around lint registration and how we
run lints in the compiler. run lints in the compiler.
The [`LintStore`] is the central piece of infrastructure, around which everything The [`LintStore`] is the central piece of infrastructure, around which
rotates. It's not available during the early parts of compilation (i.e., before everything rotates. It's not available during the early parts of compilation
TyCtxt) in most code, as we need to fill it in with all of the lints, which can only happen after (i.e., before TyCtxt) in most code, as we need to fill it in with all of the
plugin registration. lints, which can only happen after plugin registration.
## Lints vs. lint passes ## Lints vs. lint passes
There are two parts to the linting mechanism within the compiler: lints and
lint passes. Unfortunately, a lot of the documentation we have refers to both
of these as just "lints."
There are two parts to the linting mechanism within the compiler: lints and lint passes. First, we have the lint declarations themselves: this is where the name and
Unfortunately, a lot of the documentation we have refers to both of these as just "lints." default lint level and other metadata come from. These are normally defined by
way of the [`declare_lint!`] macro, which boils down to a static with type
First, we have the lint declarations themselves: this is where the name and default lint level and `&rustc_session::lint::Lint`.
other metadata come from. These are normally defined by way of the [`declare_lint!`] macro, which
boils down to a static with type `&rustc_session::lint::Lint`.
As of <!-- date: 2022-02 --> February 2022, we lint against direct declarations As of <!-- date: 2022-02 --> February 2022, we lint against direct declarations
without the use of the macro today (although this may change in the future, as without the use of the macro today (although this may change in the future, as
the macro is somewhat unwieldy to add new fields to, like all macros). the macro is somewhat unwieldy to add new fields to, like all macros).
Lint declarations don't carry any "state" - they are merely global identifiers and descriptions of Lint declarations don't carry any "state" - they are merely global identifiers
lints. We assert at runtime that they are not registered twice (by lint name). and descriptions of lints. We assert at runtime that they are not registered
twice (by lint name).
Lint passes are the meat of any lint. Notably, there is not a one-to-one relationship between Lint passes are the meat of any lint. Notably, there is not a one-to-one
lints and lint passes; a lint might not have any lint pass that emits it, it could have many, or relationship between lints and lint passes; a lint might not have any lint pass
just one -- the compiler doesn't track whether a pass is in any way associated with a particular that emits it, it could have many, or just one -- the compiler doesn't track
lint, and frequently lints are emitted as part of other work (e.g., type checking, etc.). whether a pass is in any way associated with a particular lint, and frequently
lints are emitted as part of other work (e.g., type checking, etc.).
## Registration ## Registration
### High-level overview ### High-level overview
In [`rustc_interface::register_plugins`] the [`LintStore`] is created and all
lints are registered.
In [`rustc_interface::register_plugins`] the [`LintStore`] is created and all lints are registered.
There are four 'sources' of lints: There are four 'sources' of lints:
* internal lints: lints only used by the rustc codebase * internal lints: lints only used by the rustc codebase
* builtin lints: lints built into the compiler and not provided by some outside source * builtin lints: lints built into the compiler and not provided by some outside
source
* plugin lints: lints created by plugins through the plugin system. * plugin lints: lints created by plugins through the plugin system.
* `rustc_interface::Config`[`register_lints`]: lints passed into the compiler during construction * `rustc_interface::Config`[`register_lints`]: lints passed into the compiler
during construction
Lints are registered via the [`LintStore::register_lint`] function. This should Lints are registered via the [`LintStore::register_lint`] function. This should
happen just once for any lint, or an ICE will occur. happen just once for any lint, or an ICE will occur.
Once the registration is complete, we "freeze" the lint store by placing it in an `Lrc`. Later in Once the registration is complete, we "freeze" the lint store by placing it in
the driver, it's passed into the `GlobalCtxt` constructor where it lives in an immutable form from an `Lrc`. Later in the driver, it's passed into the `GlobalCtxt` constructor
then on. where it lives in an immutable form from then on.
Lint passes are registered separately into one of the categories (pre-expansion, Lint passes are registered separately into one of the categories
early, late, late module). Passes are registered as a closure -- i.e., `impl (pre-expansion, early, late, late module). Passes are registered as a closure
Fn() -> Box<dyn X>`, where `dyn X` is either an early or late lint pass trait -- i.e., `impl Fn() -> Box<dyn X>`, where `dyn X` is either an early or late
object. When we run the lint passes, we run the closure and then invoke the lint lint pass trait object. When we run the lint passes, we run the closure and
pass methods. The lint pass methods take `&mut self` so they can keep track of state then invoke the lint pass methods. The lint pass methods take `&mut self` so
internally. they can keep track of state internally.
#### Internal lints #### Internal lints
These are lints used just by the compiler or plugins like `clippy`. They can be
found in `rustc_lint::internal`.
These are lints used just by the compiler or plugins like `clippy`. They can be found in An example of such a lint is the check that lint passes are implemented using
`rustc_lint::internal`. the `declare_lint_pass!` macro and not by hand. This is accomplished with the
An example of such a lint is the check that lint passes are implemented using the
`declare_lint_pass!` macro and not by hand. This is accomplished with the
`LINT_PASS_IMPL_WITHOUT_MACRO` lint. `LINT_PASS_IMPL_WITHOUT_MACRO` lint.
Registration of these lints happens in the [`rustc_lint::register_internals`] function which is Registration of these lints happens in the [`rustc_lint::register_internals`]
called when constructing a new lint store inside [`rustc_lint::new_lint_store`]. function which is called when constructing a new lint store inside
[`rustc_lint::new_lint_store`].
### Builtin Lints ### Builtin Lints
These are primarily described in two places: `rustc_session::lint::builtin` and These are primarily described in two places: `rustc_session::lint::builtin` and
`rustc_lint::builtin`. Often the first provides the definitions for the lints themselves, `rustc_lint::builtin`. Often the first provides the definitions for the lints
and the latter provides the lint pass definitions (and implementations), but this is not always themselves, and the latter provides the lint pass definitions (and
true. implementations), but this is not always true.
The builtin lint registration happens in the [`rustc_lint::register_builtins`] function. Just like The builtin lint registration happens in the [`rustc_lint::register_builtins`]
with internal lints, this happens inside of [`rustc_lint::new_lint_store`]. function. Just like with internal lints, this happens inside of
[`rustc_lint::new_lint_store`].
#### Plugin lints #### Plugin lints
This is one of the primary use cases remaining for plugins/drivers. Plugins are
given access to the mutable `LintStore` during registration (which happens
inside of [`rustc_interface::register_plugins`]) and they can call any
functions they need on the `LintStore`, just like rustc code.
This is one of the primary use cases remaining for plugins/drivers. Plugins are given access Plugins are intended to declare lints with the `plugin` field set to true
to the mutable `LintStore` during registration (which happens inside of (e.g., by way of the [`declare_tool_lint!`] macro), but this is purely for
[`rustc_interface::register_plugins`]) and they can call any functions they need on diagnostics and help text; otherwise plugin lints are mostly just as first
the `LintStore`, just like rustc code. class as rustc builtin lints.
Plugins are intended to declare lints with the `plugin` field set to true (e.g., by
way of the [`declare_tool_lint!`] macro), but this is purely for diagnostics and help text;
otherwise plugin lints are mostly just as first class as rustc builtin lints.
#### Driver lints #### Driver lints
These are the lints provided by drivers via the `rustc_interface::Config`
These are the lints provided by drivers via the `rustc_interface::Config` [`register_lints`] field, [`register_lints`] field, which is a callback. Drivers should, if finding it
which is a callback. Drivers should, if finding it already set, call the function currently set already set, call the function currently set within the callback they add. The
within the callback they add. The best way for drivers to get access to this is by overriding the best way for drivers to get access to this is by overriding the
`Callbacks::config` function which gives them direct access to the `Config` structure. `Callbacks::config` function which gives them direct access to the `Config`
structure.
## Compiler lint passes are combined into one pass ## Compiler lint passes are combined into one pass
Within the compiler, for performance reasons, we usually do not register dozens Within the compiler, for performance reasons, we usually do not register dozens
of lint passes. Instead, we have a single lint pass of each variety of lint passes. Instead, we have a single lint pass of each variety (e.g.,
(e.g., `BuiltinCombinedModuleLateLintPass`) which will internally call all of the `BuiltinCombinedModuleLateLintPass`) which will internally call all of the
individual lint passes; this is because then we get the benefits of static over individual lint passes; this is because then we get the benefits of static over
dynamic dispatch for each of the (often empty) trait methods. dynamic dispatch for each of the (often empty) trait methods.