rustc-dev-guide/diagnostics/diagnostic-items.html

353 lines
19 KiB
HTML

<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Diagnostic items - Rust Compiler Development Guide</title>
<!-- Custom HTML head -->
<meta name="description" content="A guide to developing the Rust compiler (rustc)">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "light";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust Compiler Development Guide</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/rust-lang/rustc-dev-guide" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/rust-lang/rustc-dev-guide/edit/master/src/diagnostics/diagnostic-items.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="diagnostic-items"><a class="header" href="#diagnostic-items">Diagnostic Items</a></h1>
<p>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
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html"><code>Symbol</code></a>s.</p>
<h2 id="finding-diagnostic-items"><a class="header" href="#finding-diagnostic-items">Finding diagnostic items</a></h2>
<p>Diagnostic items are added to items inside <code>rustc</code>/<code>std</code>/<code>core</code>/<code>alloc</code> with the
<code>rustc_diagnostic_item</code> 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 <code>cfg_attr</code> attribute to avoid compilation
errors during tests. A definition often looks like this:</p>
<pre><code class="language-rs">// This is the diagnostic item for this type vvvvvvv
#[cfg_attr(not(test), rustc_diagnostic_item = "Penguin")]
struct Penguin;
</code></pre>
<p>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
<a href="#using-diagnostic-items"><em>Using Diagnostic Items</em></a>.</p>
<h2 id="adding-diagnostic-items"><a class="header" href="#adding-diagnostic-items">Adding diagnostic items</a></h2>
<p>A new diagnostic item can be added with these two steps:</p>
<ol>
<li>
<p>Find the target item inside the Rust repo. Now add the diagnostic item as a
string via the <code>rustc_diagnostic_item</code> attribute. This can sometimes cause
compilation errors while running tests. These errors can be avoided by using
the <code>cfg_attr</code> attribute with the <code>not(test)</code> condition (it's fine adding
then for all <code>rustc_diagnostic_item</code> attributes as a preventive manner). At
the end, it should look like this:</p>
<pre><code class="language-rs">// This will be the new diagnostic item vvv
#[cfg_attr(not(test), rustc_diagnostic_item = "Cat")]
struct Cat;
</code></pre>
<p>For the naming conventions of diagnostic items, please refer to
<a href="#naming-conventions"><em>Naming Conventions</em></a>.</p>
</li>
<li><!-- date-check: Feb 2023 -->
<p>Diagnostic items in code are accessed via symbols in
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/sym/index.html"><code>rustc_span::symbol::sym</code></a>.
To add your newly-created diagnostic item,
simply open the module file,
and add the name (In this case <code>Cat</code>) at the correct point in the list.</p>
</li>
</ol>
<p>Now you can create a pull request with your changes. :tada:</p>
<blockquote>
<p>NOTE:
When using diagnostic items in other projects like Clippy,
it might take some time until the repos get synchronized.</p>
</blockquote>
<h2 id="naming-conventions"><a class="header" href="#naming-conventions">Naming conventions</a></h2>
<p>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:</p>
<ul>
<li>Types, traits, and enums are named using UpperCamelCase
(Examples: <code>Iterator</code> and <code>HashMap</code>)</li>
<li>For type names that are used multiple times,
like <code>Writer</code>,
it's good to choose a more precise name,
maybe by adding the module to it
(Example: <code>IoWriter</code>)</li>
<li>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.</li>
<li>Freestanding functions like <code>std::mem::swap()</code> should be named using
<code>snake_case</code> with one important (export) module as a prefix
(Examples: <code>mem_swap</code> and <code>cmp_max</code>)</li>
<li>Modules should usually not have a diagnostic item attached to them.
Diagnostic items were added to avoid the usage of paths,
and using them on modules would therefore most likely be counterproductive.</li>
</ul>
<h2 id="using-diagnostic-items"><a class="header" href="#using-diagnostic-items">Using diagnostic items</a></h2>
<p>In rustc, diagnostic items are looked up via <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html"><code>Symbol</code></a>s from inside the
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/sym/index.html"><code>rustc_span::symbol::sym</code></a> module. These can then be mapped to <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html"><code>DefId</code></a>s
using <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.get_diagnostic_item"><code>TyCtxt::get_diagnostic_item()</code></a> or checked if they match a <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html"><code>DefId</code></a>
using <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.is_diagnostic_item"><code>TyCtxt::is_diagnostic_item()</code></a>. When mapping from a diagnostic item to
a <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html"><code>DefId</code></a>, the method will return a <code>Option&lt;DefId&gt;</code>. This can be <code>None</code> if
either the symbol isn't a diagnostic item or the type is not registered, for
instance when compiling with <code>#[no_std]</code>.
All the following examples are based on <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html"><code>DefId</code></a>s and their usage.</p>
<h3 id="example-checking-for-a-type"><a class="header" href="#example-checking-for-a-type">Example: Checking for a type</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>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: &amp;LateContext&lt;'_&gt;, ty: Ty&lt;'_&gt;) -&gt; bool {
match ty.kind() {
ty::Adt(adt, _) =&gt; cx.tcx.is_diagnostic_item(sym::HashMap, adt.did()),
_ =&gt; false,
}
}
<span class="boring">}</span></code></pre></pre>
<h3 id="example-checking-for-a-trait-implementation"><a class="header" href="#example-checking-for-a-trait-implementation">Example: Checking for a trait implementation</a></h3>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>/// 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: &amp;LateContext&lt;'_&gt;,
def_id: DefId,
diag_item: Symbol
) -&gt; bool {
if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
return cx.tcx.is_diagnostic_item(diag_item, trait_did);
}
false
}
<span class="boring">}</span></code></pre></pre>
<h3 id="associated-types"><a class="header" href="#associated-types">Associated Types</a></h3>
<p>Associated types of diagnostic items can be accessed indirectly by first
getting the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html"><code>DefId</code></a> of the trait and then calling
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.associated_items"><code>TyCtxt::associated_items()</code></a>. This returns an <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/assoc/struct.AssocItems.html"><code>AssocItems</code></a> object which can
be used for further checks. Checkout
<a href="https://github.com/rust-lang/rust-clippy/blob/305177342fbc622c0b3cb148467bab4b9524c934/clippy_utils/src/ty.rs#L55-L72"><code>clippy_utils::ty::get_iterator_item_ty()</code></a> for an example usage of this.</p>
<h3 id="usage-in-clippy"><a class="header" href="#usage-in-clippy">Usage in Clippy</a></h3>
<p>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 <a href="https://doc.rust-lang.org/nightly/clippy/development/common_tools_writing_lints.html"><em>Common tools for writing
lints</em></a>.)</p>
<h2 id="related-issues"><a class="header" href="#related-issues">Related issues</a></h2>
<p>These are probably only interesting to people
who really want to take a deep dive into the topic :)</p>
<ul>
<li><a href="https://github.com/rust-lang/rust/pull/60966">rust#60966</a>: The Rust PR that introduced diagnostic items</li>
<li><a href="https://github.com/rust-lang/rust-clippy/issues/5393">rust-clippy#5393</a>: Clippy's tracking issue for moving away from hard coded paths to
diagnostic item</li>
</ul>
<!-- Links -->
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../diagnostics/error-codes.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../diagnostics/error-guaranteed.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../diagnostics/error-codes.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../diagnostics/error-guaranteed.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
<script src="../mermaid.min.js"></script>
<script src="../mermaid-init.js"></script>
</div>
</body>
</html>