summary refs log tree commit diff
path: root/src/doc/rustc-dev-guide
diff options
context:
space:
mode:
authorFridtjof Stoldt <xFrednet@gmail.com>2021-09-01 20:26:58 +0200
committerGitHub <noreply@github.com>2021-09-01 11:26:58 -0700
commit3ba8fa3bc2ccecdf544b5e594613f2a30133501a (patch)
treec9de5f39df3d62cdcd64ae220b7126222cf22b94 /src/doc/rustc-dev-guide
parente2f933df261a50d302a8fc5dd788f6ab23272bfa (diff)
downloadrust-3ba8fa3bc2ccecdf544b5e594613f2a30133501a.tar.gz
rust-3ba8fa3bc2ccecdf544b5e594613f2a30133501a.zip
Documenting diagnostic items with their usage and naming conventions (#1192)
* Documenting diagnostic items with their usage and naming conventions

* Fixed typos in diagnostic items documentation

Co-authored-by: Camelid <camelidcamel@gmail.com>

Co-authored-by: Camelid <camelidcamel@gmail.com>
Diffstat (limited to 'src/doc/rustc-dev-guide')
-rw-r--r--src/doc/rustc-dev-guide/src/SUMMARY.md1
-rw-r--r--src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md146
2 files changed, 147 insertions, 0 deletions
diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md
index cb3b4b404f8..33dbbf7e22a 100644
--- a/src/doc/rustc-dev-guide/src/SUMMARY.md
+++ b/src/doc/rustc-dev-guide/src/SUMMARY.md
@@ -132,6 +132,7 @@
     - [Creating Errors With SessionDiagnostic](./diagnostics/sessiondiagnostic.md)
     - [`LintStore`](./diagnostics/lintstore.md)
     - [Diagnostic Codes](./diagnostics/diagnostic-codes.md)
+    - [Diagnostic Items](./diagnostics/diagnostic-items.md)
 
 # MIR to Binaries
 
diff --git a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md
new file mode 100644
index 00000000000..efaa01cf396
--- /dev/null
+++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md
@@ -0,0 +1,146 @@
+# Diagnostic Items
+
+## Background
+
+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 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.
+
+## How To Find Diagnostic Items
+
+Diagnostic items are added to items inside `rustc`/`std`/`core` 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 errors during tests. A definition often looks like this:
+
+```rs
+// This is the diagnostic item for this type   vvvvvvv
+#[cfg_attr(not(test), rustc_diagnostic_item = "Penguin")]
+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 [*Using Diagnostic Items*](#using-diagnostic-items).
+
+## How To Add 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 string via the 
+    `rustc_diagnostic_item` attribute. This can sometimes cause compilation errors while running
+    tests. These errors can be avoided by using the `cfg_attr` attribute with the `not(test)`
+    condition (it's fine adding then for all `rustc_diagnostic_item` attributes as a preventive
+    manner). At the end, it should look like this:
+
+    ```rs
+    // This will be the new diagnostic item        vvv
+    #[cfg_attr(not(test), rustc_diagnostic_item = "Cat")]
+    struct Cat;
+    ```
+
+    For the naming conventions of diagnostic items, please refer to
+    [*Naming Conventions*](#naming-conventions).
+
+2. As of August 2021 <!-- date: 2021-08 --> 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.)
+
+## 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.
+* 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`)
+* 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.
+
+## How To Use 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.
+
+### Check For A Type 
+
+```rust
+use rustc_span::symbol::sym;
+
+/// This example checks if the given type (`ty`) has the type `HashMap` using
+/// `TyCtxt::is_diagnostic_item()`
+fn example_1(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
+    match ty.kind() {
+        ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::HashMap, adt.did),
+        _ => false,
+    }
+}
+```
+
+### Check 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.
+fn is_diag_trait_item(
+    cx: &LateContext<'_>,
+    def_id: DefId,
+    diag_item: Symbol
+) -> bool {
+    if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
+        return cx.tcx.is_diagnostic_item(diag_item, trait_did);
+    }
+    false
+}
+```
+
+### 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 who really want to
+take a deep dive into the topic :)
+
+* [rust#60966]: The Rust PR that introduced diagnostic items 
+* [rust-clippy#5393]: Clippy's tracking issue for moving away from hard coded paths to
+  diagnostic item
+
+<!-- Links -->
+
+[`rustc_span::symbol::sym`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/sym/index.html
+[`Symbol`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html
+[`DefId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html
+[`TyCtxt::get_diagnostic_item()`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.get_diagnostic_item
+[`TyCtxt::is_diagnostic_item()`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.is_diagnostic_item
+[`TyCtxt::associated_items()`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.associated_items
+[`AssocItems`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/assoc/struct.AssocItems.html
+[`clippy_utils::ty::get_iterator_item_ty()`]: https://github.com/rust-lang/rust-clippy/blob/305177342fbc622c0b3cb148467bab4b9524c934/clippy_utils/src/ty.rs#L55-L72
+[clippy-Common-tools-for-writing-lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md
+[rust#60966]: https://github.com/rust-lang/rust/pull/60966
+[rust-clippy#5393]: https://github.com/rust-lang/rust-clippy/issues/5393