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

Improve the "Diagnostic items" chapter #1427

Merged
merged 10 commits into from
Aug 13, 2022
Merged
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
79 changes: 49 additions & 30 deletions src/diagnostics/diagnostic-items.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Diagnostic Items

While writing lints it's common to check for specific types, traits and
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
Expand All @@ -7,7 +8,8 @@ rustc has introduced diagnostic items that are used to identify types via
[`Symbol`]s.

## 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`/`alloc` with the
`rustc_diagnostic_item` attribute. The item for a specific type can be found by
opening the source code in the documentation and looking for this attribute.
Note that it's often added with the `cfg_attr` attribute to avoid compilation
Expand All @@ -19,12 +21,15 @@ errors during tests. A definition often looks like this:
struct Penguin;
```

Diagnostic items are usually only added to traits, types and standalone
functions. If the goal is to check for an associated type or method, please use
the diagnostic item of the item and reference [*How To Use Diagnostic
Items*](#how-to-use-diagnostic-items).
Diagnostic items are usually only added to traits,
types,
and standalone functions.
If the goal is to check for an associated type or method,
please use the diagnostic item of the item and reference
[*Using Diagnostic Items*](#using-diagnostic-items).

## Adding diagnostic items

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
Expand All @@ -43,45 +48,55 @@ A new diagnostic item can be added with these two steps:
For the naming conventions of diagnostic items, please refer to
[*Naming Conventions*](#naming-conventions).

2. As of <!-- date-check --> February 2022, diagnostic items in code are
accessed via symbols in [`rustc_span::symbol::sym`]. To add your newly
created diagnostic item simply open the module file and add the name (In
this case `Cat`) at the correct point in the list.
2. <!-- date-check: Aug 2022 -->
Diagnostic items in code are accessed via symbols in
[`rustc_span::symbol::sym`].
To add your newly-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 items in other projects like Clippy, it might take some time
until the repos get synchronized.)
Now you can create a pull request with your changes. :tada:

> NOTE:
> When using diagnostic items in other projects like Clippy,
> it might take some time until the repos get synchronized.

## 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:

* Types, traits and enums are named using UpperCamelCase (Examples: `Iterator`,
* `HashMap`, ...)
* For type names that are used multiple times like `Writer` it's good to choose
a more precise name, maybe by adding the module to it. (Example: `IoWriter`)
* Associated items should not get their own diagnostic items, but instead be
accessed indirectly by the diagnostic item of the type they're originating
from.

Diagnostic items don't have a naming convention yet.
Following are some guidelines that should be used in future,
but might differ from existing names:

* Types, traits, and enums are named using UpperCamelCase
(Examples: `Iterator` and `HashMap`)
* For type names that are used multiple times,
like `Writer`,
it's good to choose a more precise name,
maybe by adding the module to it
(Example: `IoWriter`)
* Associated items should not get their own diagnostic items,
but instead be accessed indirectly by the diagnostic item
of the type they're originating from.
* Freestanding functions like `std::mem::swap()` should be named using
`snake_case` with one important (export) module as a prefix (Example:
`mem_swap`, `cmp_max`)
`snake_case` with one important (export) module as a prefix
(Examples: `mem_swap` and `cmp_max`)
* Modules should usually not have a diagnostic item attached to them.
Diagnostic items were added to avoid the usage of paths, using them on
modules would therefore most likely to be counterproductive.
Diagnostic items were added to avoid the usage of paths,
and using them on modules would therefore most likely be counterproductive.

## Using diagnostic items

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 [`TyCtxt::get_diagnostic_item()`] or checked if they match a [`DefId`]
using [`TyCtxt::is_diagnostic_item()`]. When mapping from a diagnostic item to
a [`DefId`], the method will return a `Option<DefId>`. This can be `None` if
either the symbol isn't a diagnostic item or the type is not registered, for
instance when compiling with `#[no_std]`. All following examples are based on
[`DefId`]s and their usage.
instance when compiling with `#[no_std]`.
All the following examples are based on [`DefId`]s and their usage.

### Example: Checking for a type

```rust
use rustc_span::symbol::sym;

Expand All @@ -96,6 +111,7 @@ fn example_1(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
```

### Example: Checking for a trait implementation

```rust
/// This example checks if a given [`DefId`] from a method is part of a trait
/// implementation defined by a diagnostic item.
Expand All @@ -112,20 +128,23 @@ fn is_diag_trait_item(
```

### Associated Types

Associated types of diagnostic items can be accessed indirectly by first
getting the [`DefId`] of the trait and then calling
[`TyCtxt::associated_items()`]. This returns an [`AssocItems`] object which can
be used for further checks. Checkout
[`clippy_utils::ty::get_iterator_item_ty()`] for an example usage of this.

### 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].)

## Related issues
This lists some related issues. These are probably only interesting to people

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
Expand Down