|
| 1 | +# Safety-Critical Rust Consortium Meeting on 2026-05-27 @ 10am EDT / 4pm CEST |
| 2 | + |
| 3 | +Expert session with Jason Newcomb of the Rust Project Clippy Team (t-clippy) |
| 4 | + |
| 5 | +| Search Key | Description | |
| 6 | +| :---- | :---- | |
| 7 | +| \[todo\] | Action Item | |
| 8 | +| \[decision\] | Something decided on | |
| 9 | +| \[important\] | Key information | |
| 10 | + |
| 11 | +## Agenda |
| 12 | + |
| 13 | +1. Introduction to t-clippy |
| 14 | +2. Practical guide to writing Clippy lints |
| 15 | + |
| 16 | +## Check-in area |
| 17 | + |
| 18 | +**Please add your name, and an emoji that describes your day.** |
| 19 | + |
| 20 | +* David Svoboda (-: |
| 21 | +* Jason Newcomb |
| 22 | +* Michael Henn 🥵 |
| 23 | +* Manuel Hatzl ☀️ |
| 24 | +* Samuel Wright 🌞 |
| 25 | +* Arshad Mahmood 🌅 |
| 26 | +* Max Jacinto 🍀 |
| 27 | +* Pete LeVasseur 🦀🦺 |
| 28 | +* Mira Baumann 🧵 |
| 29 | +* Raiki Tamura🏫 |
| 30 | +* Achim Kriso 🦆 |
| 31 | +* Mikhail Antoshkin 💤 |
| 32 | +* William Barsse |
| 33 | +* Marcos Borges 😎 |
| 34 | +* Christof Petig 🥵 |
| 35 | + |
| 36 | +**Notetaker:** |
| 37 | + |
| 38 | +* Pete LeVasseur |
| 39 | + |
| 40 | +## Housekeeping section |
| 41 | + |
| 42 | +* |
| 43 | + |
| 44 | +## Tasks |
| 45 | + |
| 46 | +* |
| 47 | + |
| 48 | +## Meeting Minutes |
| 49 | + |
| 50 | +## Introduction to t-clippy |
| 51 | + |
| 52 | +Clippy Book / Getting Started Guide |
| 53 | + |
| 54 | +* [https://doc.rust-lang.org/nightly/clippy/development/infrastructure/book.html](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/book.html) |
| 55 | + |
| 56 | +Who has worked on/with Clippy? |
| 57 | + |
| 58 | +* Pete LeVasseur |
| 59 | +* Arshad Mahmood |
| 60 | +* Achim Kriso |
| 61 | + |
| 62 | +Who has worked on/with static analysis tools in the past? |
| 63 | + |
| 64 | +* David Svoboda |
| 65 | + |
| 66 | +Differing levels of where you can “hook into” |
| 67 | + |
| 68 | +* Syntax/AST |
| 69 | +* HIR |
| 70 | +* MIR |
| 71 | + |
| 72 | +Can we have the lints be put into their own Safety-Critical Rust grouping (to Jason Newcomb)? |
| 73 | + |
| 74 | +* Jason will bring this forward to the team and discuss the idea |
| 75 | +* Might or might not be a need for this depending on (1) how many lints we are writing (2) how |
| 76 | + |
| 77 | +Can we have lints that overlap between different groups? |
| 78 | + |
| 79 | +* Technically possible today |
| 80 | +* Prefer to not do this |
| 81 | +* Jason would need to bring this back to the team to discuss the usecase of safety-critical to be able to “turn on all the things” |
| 82 | + |
| 83 | +Starting point |
| 84 | + |
| 85 | +* `cargo dev new_lint -h` for reading how to create a new lint |
| 86 | +* Also read: [https://doc.rust-lang.org/nightly/clippy/development/infrastructure/book.html](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/book.html) |
| 87 | +* Basic scaffolding will be set up |
| 88 | + * “Lint pass” to hook into compiler’s infrastructure |
| 89 | + * Could be any number of lints in a single lint pass |
| 90 | +* Lint pass runs as visitor over entire HIR tree |
| 91 | + * Can then define `check_expr` on the `LateLintPass` which will run over every expression node in the HIR tree |
| 92 | + * Can then define `check_body` as well as an alternative type of item to check |
| 93 | + * Many, many hooks are available to you in here to hook into the visitor |
| 94 | + |
| 95 | +In `check_expr`: |
| 96 | + |
| 97 | +* Tend to first do some sort of filtering to find the type of expression that you’d like to lint on |
| 98 | + * `match e.kind { /* .. */ }` |
| 99 | + * Can then filter onto e.g. `ExprKind::Path` |
| 100 | + * Can allow for destructuring and further filtering |
| 101 | +* Entails reading a lot of compiler documentation to understand how to check over HIR |
| 102 | + * [https://doc.rust-lang.org/nightly/nightly-rustc/](https://doc.rust-lang.org/nightly/nightly-rustc/) |
| 103 | + * [https://rustc-dev-guide.rust-lang.org/](https://rustc-dev-guide.rust-lang.org/) |
| 104 | + * [https://github.com/rust-lang/rust-clippy/tree/master/book/src/development](https://github.com/rust-lang/rust-clippy/tree/master/book/src/development) |
| 105 | + * Some things are documented, some things are not |
| 106 | + * May, at times, need to look at compiler source code |
| 107 | +* Can further filter, e.g. |
| 108 | + * `match p { QPath::Resolved(/* .. */) => { /* .. */ }, /* .. */ }` |
| 109 | +* Many, many hooks are available to you in here |
| 110 | + |
| 111 | +The tests and how to work with them |
| 112 | + |
| 113 | +* Ends up being named the same as the lint that you named |
| 114 | +* All lints run on all files (dogfooding) |
| 115 | +* If a new lint triggers on other files, you can tell them to ignore the new lint |
| 116 | + * When writing test, then default enabled things run at the same time |
| 117 | + * Could cause it to take some time to run at first |
| 118 | + * Normally then handle this with a targeted `#![allow(clippy::other_failing_lint_we_dont_care_about)]` |
| 119 | +* There are `your_lint_here.stderr` files which contain the expected output of the lint that this is checked against |
| 120 | + |
| 121 | +The mindset |
| 122 | + |
| 123 | +* Write a test which is “normal” and represents where the lint should fire |
| 124 | +* Write the the lint logic |
| 125 | +* Go run the lints |
| 126 | +* Note that it may fail or not catch what we want |
| 127 | +* Once it does, run the `bless` command and go back and write another test, then flesh out the lint more in a loop |
| 128 | + |
| 129 | +Question: Is there some kind of workflow / secret that will not be on the documentation? |
| 130 | + |
| 131 | +* No, not really |
| 132 | +* All of what Jason is walking us through is available in the documentation |
| 133 | +* Some of the things “not in” the Clippy documentation could be where as a lint implementor you have to get into how the compiler works, ASTs, HIR, MIR |
| 134 | + |
| 135 | +Question: How about a more complex lint? Like constant propagation? |
| 136 | + |
| 137 | +* Please hang tight, we’ll come back to this |
| 138 | + |
| 139 | +`LateLintPass` details for HIR |
| 140 | + |
| 141 | +* Doesn’t contain type information |
| 142 | +* Second parameter of `LateContext` will give useful information |
| 143 | + * `cx.typeck_results()` will contain all the type checking information |
| 144 | + * Can work with this further, e.g. `typeck.expr_ty(e)` |
| 145 | +* There is a helper function to use for checking on trait implementation |
| 146 | + * `implements_trait!()` |
| 147 | +* Also have access to e.g. `cx.tcx.lang_items().eq_trait()` for getting the trait’s definition ID |
| 148 | +* Depending on if the trait has generics, there’s now an assert which forces passing this information that’s relevant in |
| 149 | +* Can resolve method calls, e.g. `ExprKind::MethodCall(/* .. */) => { def_id = typeck.type_dependent_def_id(e.hir_id) }` |
| 150 | + * This information must be obtained like above |
| 151 | +* Other useful things like diagnostic items, e.g. `cx.tcs.is_diagnostic_item(name, did)` |
| 152 | + * You’ll need to check the compiler documentation for this |
| 153 | + * Jump to definition will take you to compiler source so you can get that info that’s decorated on the item: `rustc_diagnostic_item` |
| 154 | +* Helpful at times when filtering to “do the opposite of what the check is for” |
| 155 | +* When referring to typechecked item, goes back to the trait, not the implementation of the trait |
| 156 | + * If “is this String’s implementation of something?” have to check type and Definition ID (def\_id) |
| 157 | + |
| 158 | +`LateLintPass` details for MIR |
| 159 | + |
| 160 | +* `check_fn()` or `check_body()` |
| 161 | +* `check_fn()` may also handle closures |
| 162 | +* First need to write a check for if MIR can be obtained |
| 163 | + * If something can never be satisfied, then the MIR body never exists |
| 164 | + * Would crash if trying to obtain |
| 165 | +* Work on optimized MIR |
| 166 | +* Perform whole MIR lint check on the body |
| 167 | +* For data flow analysis there’s fixed point analysis framework that allows to go MIR block by MIR block using the `Analysis` trait ([rustc\_mir\_dataflow](https://doc.rust-lang.org/beta/nightly-rustc/rustc_mir_dataflow/)) |
| 168 | + * Propagate until you run out of checks/changes to perform |
| 169 | +* Not a lot specific to doing const propagation |
| 170 | + * Likely need to write your own code to handle this |
| 171 | + |
| 172 | +Because lints are run on macro-expanded code |
| 173 | + |
| 174 | +* You have to do some things to figure out if that’s the case |
| 175 | +* On each node you have the span |
| 176 | + * `e.span.ctxt()` \=\> syntax context |
| 177 | +* Unique syntax context for each |
| 178 | + * macro expansion and |
| 179 | + * AST \=\> HIR lowering |
| 180 | +* Syntax context allows for telling when this expansion or lowering has occurred |
| 181 | + * `e.span.ctxt().outer_expn_data()` |
| 182 | + * Would end up with two different macro expansion context |
| 183 | +* When writing lints you may need to keep track of this; these can show up anywhere |
| 184 | + * Any given `x` and `y` might originate from a macro expansion |
| 185 | + * For style lints this matters and you have to compare context a lot along the way |
| 186 | + * `e.span.ctxt() == p.span.ctxt()` |
| 187 | + * Checking if the context of the parent thing is the same as the expression under investigation |
| 188 | + |
| 189 | +Ways to solicit help / support: |
| 190 | + |
| 191 | +* If writing a lint and stuck \=\> open a draft PR |
| 192 | +* If stuck, can open a topic on the Rust Zulip’s clippy channel |
| 193 | + |
| 194 | +## Material |
| 195 | + |
| 196 | +Any material to read before the meeting should be included here. |
| 197 | + |
| 198 | +* |
| 199 | + |
0 commit comments