blob: e7cca58e4f19f09cfcf9a654b4924002149f03c5 [file] [log] [blame] [edit]
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>LintStore - 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 -->
<link rel="stylesheet" href="../pagetoc.css">
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "light";
const default_dark_theme = "navy";
window.path_to_searchindex_js = "../searchindex.js";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd></kbd> or <kbd></kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<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 = false;
}
if (sidebar === 'visible') {
sidebar_toggle.checked = true;
} else {
html.classList.remove('sidebar-visible');
}
</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 (`/`)" 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/main/src/diagnostics/lintstore.md" title="Suggest an edit" aria-label="Suggest an edit" rel="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">
<div class="search-wrapper">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
<div class="spinner-wrapper">
<i class="fa fa-spinner fa-spin"></i>
</div>
</div>
</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="lints"><a class="header" href="#lints">Lints</a></h1>
<p>This page documents some of the machinery around lint registration and how we
run lints in the compiler.</p>
<p>The <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html"><code>LintStore</code></a> is the central piece of infrastructure, around which
everything rotates. The <code>LintStore</code> is held as part of the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/struct.Session.html"><code>Session</code></a>, and it
gets populated with the list of lints shortly after the <code>Session</code> is created.</p>
<h2 id="lints-vs-lint-passes"><a class="header" href="#lints-vs-lint-passes">Lints vs. lint passes</a></h2>
<p>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."</p>
<p>First, we have the lint declarations themselves,
and this is where the name and default lint level and other metadata come from.
These are normally defined by way of the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/macro.declare_lint.html"><code>declare_lint!</code></a> macro,
which boils down to a static with type <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/struct.Lint.html"><code>&amp;rustc_lint_defs::Lint</code></a>
(although this may change in the future,
as the macro is somewhat unwieldy to add new fields to,
like all macros).</p>
<p>As of <!-- date-check --> Aug 2022,
we lint against direct declarations without the use of the macro.</p>
<p>Lint declarations don't carry any "state" - they are merely global identifiers
and descriptions of lints. We assert at runtime that they are not registered
twice (by lint name).</p>
<p>Lint passes are the meat of any lint. Notably, there is not a one-to-one
relationship between lints and lint passes; a lint might not have any lint pass
that emits it, it could have many, or just one -- the compiler doesn't track
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.).</p>
<h2 id="registration"><a class="header" href="#registration">Registration</a></h2>
<h3 id="high-level-overview"><a class="header" href="#high-level-overview">High-level overview</a></h3>
<p>In <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/index.html#reexport.run_compiler"><code>rustc_interface::run_compiler</code></a>,
the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html"><code>LintStore</code></a> is created,
and all lints are registered.</p>
<p>There are three 'sources' of lints:</p>
<ul>
<li>internal lints: lints only used by the rustc codebase</li>
<li>builtin lints: lints built into the compiler and not provided by some outside
source</li>
<li><code>rustc_interface::Config</code><a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html#structfield.register_lints"><code>register_lints</code></a>: lints passed into the compiler
during construction</li>
</ul>
<p>Lints are registered via the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_lints"><code>LintStore::register_lint</code></a> function. This should
happen just once for any lint, or an ICE will occur.</p>
<p>Once the registration is complete, we "freeze" the lint store by placing it in
an <code>Arc</code>.</p>
<p>Lint passes are registered separately into one of the categories
(pre-expansion, early, late, late module). Passes are registered as a closure
-- i.e., <code>impl Fn() -&gt; Box&lt;dyn X&gt;</code>, where <code>dyn X</code> is either an early or late
lint pass trait object. When we run the lint passes, we run the closure and
then invoke the lint pass methods. The lint pass methods take <code>&amp;mut self</code> so
they can keep track of state internally.</p>
<h4 id="internal-lints"><a class="header" href="#internal-lints">Internal lints</a></h4>
<p>These are lints used just by the compiler or drivers like <code>clippy</code>. They can be
found in <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/internal/index.html"><code>rustc_lint::internal</code></a>.</p>
<p>An example of such a lint is the check that lint passes are implemented using
the <code>declare_lint_pass!</code> macro and not by hand. This is accomplished with the
<code>LINT_PASS_IMPL_WITHOUT_MACRO</code> lint.</p>
<p>Registration of these lints happens in the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_internals.html"><code>rustc_lint::register_internals</code></a>
function which is called when constructing a new lint store inside
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.new_lint_store.html"><code>rustc_lint::new_lint_store</code></a>.</p>
<h4 id="builtin-lints"><a class="header" href="#builtin-lints">Builtin Lints</a></h4>
<p>These are primarily described in two places,
<code>rustc_lint_defs::builtin</code> and <code>rustc_lint::builtin</code>.
Often the first provides the definitions for the lints themselves,
and the latter provides the lint pass definitions (and implementations),
but this is not always true.</p>
<p>The builtin lint registration happens in
the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.register_builtins.html"><code>rustc_lint::register_builtins</code></a> function.
Just like with internal lints,
this happens inside of <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/fn.new_lint_store.html"><code>rustc_lint::new_lint_store</code></a>.</p>
<h4 id="driver-lints"><a class="header" href="#driver-lints">Driver lints</a></h4>
<p>These are the lints provided by drivers via the <code>rustc_interface::Config</code>
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html#structfield.register_lints"><code>register_lints</code></a> field, which is a callback. Drivers should, if finding it
already set, call the function currently set within the callback they add. The
best way for drivers to get access to this is by overriding the
<code>Callbacks::config</code> function which gives them direct access to the <code>Config</code>
structure.</p>
<h2 id="compiler-lint-passes-are-combined-into-one-pass"><a class="header" href="#compiler-lint-passes-are-combined-into-one-pass">Compiler lint passes are combined into one pass</a></h2>
<p>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 (e.g.,
<code>BuiltinCombinedModuleLateLintPass</code>) which will internally call all of the
individual lint passes; this is because then we get the benefits of static over
dynamic dispatch for each of the (often empty) trait methods.</p>
<p>Ideally, we'd not have to do this, since it adds to the complexity of
understanding the code. However, with the current type-erased lint store
approach, it is beneficial to do so for performance reasons.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../diagnostics/translation.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-codes.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/translation.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-codes.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>
<script src="../pagetoc.js"></script>
</div>
</body>
</html>