]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | //! A group of attributes that can be attached to Rust code in order |
2 | //! to generate a clippy lint detecting said code automatically. | |
3 | ||
a2a8927a | 4 | use clippy_utils::{get_attr, higher}; |
f20569fa | 5 | use rustc_ast::ast::{LitFloatType, LitKind}; |
a2a8927a | 6 | use rustc_ast::LitIntType; |
f20569fa XL |
7 | use rustc_data_structures::fx::FxHashMap; |
8 | use rustc_hir as hir; | |
f2b60f7d FG |
9 | use rustc_hir::{ |
10 | ArrayLen, BindingAnnotation, Closure, ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, | |
11 | }; | |
f20569fa | 12 | use rustc_lint::{LateContext, LateLintPass, LintContext}; |
f20569fa | 13 | use rustc_session::{declare_lint_pass, declare_tool_lint}; |
a2a8927a | 14 | use rustc_span::symbol::{Ident, Symbol}; |
2b03887a | 15 | use std::cell::Cell; |
a2a8927a | 16 | use std::fmt::{Display, Formatter, Write as _}; |
f20569fa XL |
17 | |
18 | declare_clippy_lint! { | |
94222f64 XL |
19 | /// ### What it does |
20 | /// Generates clippy code that detects the offending pattern | |
f20569fa | 21 | /// |
94222f64 | 22 | /// ### Example |
f20569fa XL |
23 | /// ```rust,ignore |
24 | /// // ./tests/ui/my_lint.rs | |
25 | /// fn foo() { | |
26 | /// // detect the following pattern | |
27 | /// #[clippy::author] | |
28 | /// if x == 42 { | |
29 | /// // but ignore everything from here on | |
30 | /// #![clippy::author = "ignore"] | |
31 | /// } | |
32 | /// () | |
33 | /// } | |
34 | /// ``` | |
35 | /// | |
36 | /// Running `TESTNAME=ui/my_lint cargo uitest` will produce | |
37 | /// a `./tests/ui/new_lint.stdout` file with the generated code: | |
38 | /// | |
39 | /// ```rust,ignore | |
40 | /// // ./tests/ui/new_lint.stdout | |
2b03887a FG |
41 | /// if ExprKind::If(ref cond, ref then, None) = item.kind |
42 | /// && let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind | |
43 | /// && let ExprKind::Path(ref path) = left.kind | |
44 | /// && let ExprKind::Lit(ref lit) = right.kind | |
45 | /// && let LitKind::Int(42, _) = lit.node | |
46 | /// { | |
47 | /// // report your lint here | |
f20569fa XL |
48 | /// } |
49 | /// ``` | |
50 | pub LINT_AUTHOR, | |
51 | internal_warn, | |
52 | "helper for writing lints" | |
53 | } | |
54 | ||
55 | declare_lint_pass!(Author => [LINT_AUTHOR]); | |
56 | ||
a2a8927a XL |
57 | /// Writes a line of output with indentation added |
58 | macro_rules! out { | |
59 | ($($t:tt)*) => { | |
60 | println!(" {}", format_args!($($t)*)) | |
61 | }; | |
62 | } | |
63 | ||
64 | /// The variables passed in are replaced with `&Binding`s where the `value` field is set | |
65 | /// to the original value of the variable. The `name` field is set to the name of the variable | |
66 | /// (using `stringify!`) and is adjusted to avoid duplicate names. | |
67 | /// Note that the `Binding` may be printed directly to output the `name`. | |
68 | macro_rules! bind { | |
69 | ($self:ident $(, $name:ident)+) => { | |
70 | $(let $name = & $self.bind(stringify!($name), $name);)+ | |
71 | }; | |
72 | } | |
73 | ||
04454e1e | 74 | /// Transforms the given `Option<T>` variables into `OptionPat<Binding<T>>`. |
a2a8927a XL |
75 | /// This displays as `Some($name)` or `None` when printed. The name of the inner binding |
76 | /// is set to the name of the variable passed to the macro. | |
77 | macro_rules! opt_bind { | |
78 | ($self:ident $(, $name:ident)+) => { | |
79 | $(let $name = OptionPat::new($name.map(|o| $self.bind(stringify!($name), o)));)+ | |
80 | }; | |
81 | } | |
82 | ||
83 | /// Creates a `Binding` that accesses the field of an existing `Binding` | |
84 | macro_rules! field { | |
85 | ($binding:ident.$field:ident) => { | |
86 | &Binding { | |
87 | name: $binding.name.to_string() + stringify!(.$field), | |
88 | value: $binding.value.$field, | |
89 | } | |
90 | }; | |
91 | } | |
92 | ||
2b03887a FG |
93 | /// Print a condition of a let chain, `chain!(self, "let Some(x) = y")` will print |
94 | /// `if let Some(x) = y` on the first call and ` && let Some(x) = y` thereafter | |
95 | macro_rules! chain { | |
96 | ($self:ident, $($t:tt)*) => { | |
97 | if $self.first.take() { | |
98 | println!("if {}", format_args!($($t)*)); | |
99 | } else { | |
100 | println!(" && {}", format_args!($($t)*)); | |
101 | } | |
102 | } | |
f20569fa XL |
103 | } |
104 | ||
105 | impl<'tcx> LateLintPass<'tcx> for Author { | |
106 | fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { | |
a2a8927a | 107 | check_item(cx, item.hir_id()); |
f20569fa XL |
108 | } |
109 | ||
110 | fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { | |
a2a8927a | 111 | check_item(cx, item.hir_id()); |
f20569fa XL |
112 | } |
113 | ||
114 | fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { | |
a2a8927a | 115 | check_item(cx, item.hir_id()); |
f20569fa XL |
116 | } |
117 | ||
a2a8927a XL |
118 | fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { |
119 | check_node(cx, arm.hir_id, |v| { | |
120 | v.arm(&v.bind("arm", arm)); | |
121 | }); | |
f20569fa XL |
122 | } |
123 | ||
124 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { | |
a2a8927a XL |
125 | check_node(cx, expr.hir_id, |v| { |
126 | v.expr(&v.bind("expr", expr)); | |
127 | }); | |
f20569fa XL |
128 | } |
129 | ||
130 | fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { | |
94222f64 XL |
131 | match stmt.kind { |
132 | StmtKind::Expr(e) | StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return, | |
133 | _ => {}, | |
134 | } | |
a2a8927a XL |
135 | check_node(cx, stmt.hir_id, |v| { |
136 | v.stmt(&v.bind("stmt", stmt)); | |
137 | }); | |
138 | } | |
139 | } | |
140 | ||
141 | fn check_item(cx: &LateContext<'_>, hir_id: HirId) { | |
142 | let hir = cx.tcx.hir(); | |
2b03887a | 143 | if let Some(body_id) = hir.maybe_body_owned_by(hir_id.expect_owner().def_id) { |
a2a8927a | 144 | check_node(cx, hir_id, |v| { |
f2b60f7d | 145 | v.expr(&v.bind("expr", hir.body(body_id).value)); |
a2a8927a XL |
146 | }); |
147 | } | |
148 | } | |
149 | ||
150 | fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) { | |
151 | if has_attr(cx, hir_id) { | |
a2a8927a | 152 | f(&PrintVisitor::new(cx)); |
2b03887a FG |
153 | println!("{{"); |
154 | println!(" // report your lint here"); | |
155 | println!("}}"); | |
f20569fa | 156 | } |
a2a8927a | 157 | } |
f20569fa | 158 | |
a2a8927a XL |
159 | struct Binding<T> { |
160 | name: String, | |
161 | value: T, | |
162 | } | |
163 | ||
164 | impl<T> Display for Binding<T> { | |
165 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | |
166 | f.write_str(&self.name) | |
167 | } | |
168 | } | |
169 | ||
170 | struct OptionPat<T> { | |
171 | pub opt: Option<T>, | |
172 | } | |
173 | ||
174 | impl<T> OptionPat<T> { | |
175 | fn new(opt: Option<T>) -> Self { | |
176 | Self { opt } | |
177 | } | |
178 | ||
179 | fn if_some(&self, f: impl Fn(&T)) { | |
180 | if let Some(t) = &self.opt { | |
181 | f(t); | |
182 | } | |
183 | } | |
184 | } | |
185 | ||
186 | impl<T: Display> Display for OptionPat<T> { | |
187 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | |
188 | match &self.opt { | |
189 | None => f.write_str("None"), | |
190 | Some(node) => write!(f, "Some({node})"), | |
f20569fa | 191 | } |
f20569fa XL |
192 | } |
193 | } | |
194 | ||
a2a8927a XL |
195 | struct PrintVisitor<'a, 'tcx> { |
196 | cx: &'a LateContext<'tcx>, | |
197 | /// Fields are the current index that needs to be appended to pattern | |
198 | /// binding names | |
2b03887a FG |
199 | ids: Cell<FxHashMap<&'static str, u32>>, |
200 | /// Currently at the first condition in the if chain | |
201 | first: Cell<bool>, | |
a2a8927a XL |
202 | } |
203 | ||
204 | #[allow(clippy::unused_self)] | |
205 | impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { | |
206 | fn new(cx: &'a LateContext<'tcx>) -> Self { | |
f20569fa | 207 | Self { |
a2a8927a | 208 | cx, |
2b03887a FG |
209 | ids: Cell::default(), |
210 | first: Cell::new(true), | |
f20569fa XL |
211 | } |
212 | } | |
213 | ||
a2a8927a XL |
214 | fn next(&self, s: &'static str) -> String { |
215 | let mut ids = self.ids.take(); | |
216 | let out = match *ids.entry(s).and_modify(|n| *n += 1).or_default() { | |
217 | // first usage of the name, use it as is | |
218 | 0 => s.to_string(), | |
219 | // append a number starting with 1 | |
220 | n => format!("{s}{n}"), | |
221 | }; | |
222 | self.ids.set(ids); | |
223 | out | |
224 | } | |
225 | ||
226 | fn bind<T>(&self, name: &'static str, value: T) -> Binding<T> { | |
227 | let name = self.next(name); | |
228 | Binding { name, value } | |
229 | } | |
230 | ||
231 | fn option<T: Copy>(&self, option: &Binding<Option<T>>, name: &'static str, f: impl Fn(&Binding<T>)) { | |
232 | match option.value { | |
2b03887a | 233 | None => chain!(self, "{option}.is_none()"), |
a2a8927a XL |
234 | Some(value) => { |
235 | let value = &self.bind(name, value); | |
2b03887a | 236 | chain!(self, "let Some({value}) = {option}"); |
a2a8927a | 237 | f(value); |
f20569fa XL |
238 | }, |
239 | } | |
240 | } | |
241 | ||
a2a8927a XL |
242 | fn slice<T>(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) { |
243 | if slice.value.is_empty() { | |
2b03887a | 244 | chain!(self, "{slice}.is_empty()"); |
f20569fa | 245 | } else { |
2b03887a | 246 | chain!(self, "{slice}.len() == {}", slice.value.len()); |
a2a8927a XL |
247 | for (i, value) in slice.value.iter().enumerate() { |
248 | let name = format!("{slice}[{i}]"); | |
249 | f(&Binding { name, value }); | |
250 | } | |
f20569fa XL |
251 | } |
252 | } | |
f20569fa | 253 | |
a2a8927a XL |
254 | fn destination(&self, destination: &Binding<hir::Destination>) { |
255 | self.option(field!(destination.label), "label", |label| { | |
256 | self.ident(field!(label.ident)); | |
257 | }); | |
258 | } | |
259 | ||
260 | fn ident(&self, ident: &Binding<Ident>) { | |
2b03887a | 261 | chain!(self, "{ident}.as_str() == {:?}", ident.value.as_str()); |
a2a8927a XL |
262 | } |
263 | ||
264 | fn symbol(&self, symbol: &Binding<Symbol>) { | |
2b03887a | 265 | chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); |
a2a8927a | 266 | } |
f20569fa | 267 | |
a2a8927a XL |
268 | fn qpath(&self, qpath: &Binding<&QPath<'_>>) { |
269 | if let QPath::LangItem(lang_item, ..) = *qpath.value { | |
2b03887a | 270 | chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); |
a2a8927a | 271 | } else { |
2b03887a | 272 | chain!(self, "match_qpath({qpath}, &[{}])", path_to_string(qpath.value)); |
a2a8927a XL |
273 | } |
274 | } | |
275 | ||
276 | fn lit(&self, lit: &Binding<&Lit>) { | |
2b03887a | 277 | let kind = |kind| chain!(self, "let LitKind::{kind} = {lit}.node"); |
a2a8927a XL |
278 | macro_rules! kind { |
279 | ($($t:tt)*) => (kind(format_args!($($t)*))); | |
280 | } | |
281 | ||
282 | match lit.value.node { | |
283 | LitKind::Bool(val) => kind!("Bool({val:?})"), | |
284 | LitKind::Char(c) => kind!("Char({c:?})"), | |
f2b60f7d | 285 | LitKind::Err => kind!("Err"), |
a2a8927a XL |
286 | LitKind::Byte(b) => kind!("Byte({b})"), |
287 | LitKind::Int(i, suffix) => { | |
288 | let int_ty = match suffix { | |
289 | LitIntType::Signed(int_ty) => format!("LitIntType::Signed(IntTy::{int_ty:?})"), | |
290 | LitIntType::Unsigned(uint_ty) => format!("LitIntType::Unsigned(UintTy::{uint_ty:?})"), | |
291 | LitIntType::Unsuffixed => String::from("LitIntType::Unsuffixed"), | |
292 | }; | |
293 | kind!("Int({i}, {int_ty})"); | |
294 | }, | |
295 | LitKind::Float(_, suffix) => { | |
296 | let float_ty = match suffix { | |
297 | LitFloatType::Suffixed(suffix_ty) => format!("LitFloatType::Suffixed(FloatTy::{suffix_ty:?})"), | |
298 | LitFloatType::Unsuffixed => String::from("LitFloatType::Unsuffixed"), | |
299 | }; | |
300 | kind!("Float(_, {float_ty})"); | |
301 | }, | |
f25598a0 | 302 | LitKind::ByteStr(ref vec, _) => { |
a2a8927a XL |
303 | bind!(self, vec); |
304 | kind!("ByteStr(ref {vec})"); | |
2b03887a | 305 | chain!(self, "let [{:?}] = **{vec}", vec.value); |
a2a8927a XL |
306 | }, |
307 | LitKind::Str(s, _) => { | |
308 | bind!(self, s); | |
309 | kind!("Str({s}, _)"); | |
310 | self.symbol(s); | |
311 | }, | |
312 | } | |
313 | } | |
314 | ||
315 | fn arm(&self, arm: &Binding<&hir::Arm<'_>>) { | |
316 | self.pat(field!(arm.pat)); | |
317 | match arm.value.guard { | |
2b03887a | 318 | None => chain!(self, "{arm}.guard.is_none()"), |
a2a8927a XL |
319 | Some(hir::Guard::If(expr)) => { |
320 | bind!(self, expr); | |
2b03887a | 321 | chain!(self, "let Some(Guard::If({expr})) = {arm}.guard"); |
a2a8927a XL |
322 | self.expr(expr); |
323 | }, | |
923072b8 FG |
324 | Some(hir::Guard::IfLet(let_expr)) => { |
325 | bind!(self, let_expr); | |
2b03887a | 326 | chain!(self, "let Some(Guard::IfLet({let_expr}) = {arm}.guard"); |
923072b8 FG |
327 | self.pat(field!(let_expr.pat)); |
328 | self.expr(field!(let_expr.init)); | |
a2a8927a XL |
329 | }, |
330 | } | |
331 | self.expr(field!(arm.body)); | |
332 | } | |
f20569fa XL |
333 | |
334 | #[allow(clippy::too_many_lines)] | |
a2a8927a XL |
335 | fn expr(&self, expr: &Binding<&hir::Expr<'_>>) { |
336 | if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) { | |
337 | bind!(self, condition, body); | |
2b03887a FG |
338 | chain!( |
339 | self, | |
340 | "let Some(higher::While {{ condition: {condition}, body: {body} }}) \ | |
341 | = higher::While::hir({expr})" | |
a2a8927a XL |
342 | ); |
343 | self.expr(condition); | |
344 | self.expr(body); | |
345 | return; | |
346 | } | |
347 | ||
348 | if let Some(higher::WhileLet { | |
349 | let_pat, | |
350 | let_expr, | |
351 | if_then, | |
352 | }) = higher::WhileLet::hir(expr.value) | |
353 | { | |
354 | bind!(self, let_pat, let_expr, if_then); | |
2b03887a FG |
355 | chain!( |
356 | self, | |
357 | "let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ | |
358 | = higher::WhileLet::hir({expr})" | |
a2a8927a XL |
359 | ); |
360 | self.pat(let_pat); | |
361 | self.expr(let_expr); | |
362 | self.expr(if_then); | |
363 | return; | |
364 | } | |
365 | ||
366 | if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) { | |
367 | bind!(self, pat, arg, body); | |
2b03887a FG |
368 | chain!( |
369 | self, | |
370 | "let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ | |
371 | = higher::ForLoop::hir({expr})" | |
a2a8927a XL |
372 | ); |
373 | self.pat(pat); | |
374 | self.expr(arg); | |
375 | self.expr(body); | |
376 | return; | |
377 | } | |
378 | ||
2b03887a | 379 | let kind = |kind| chain!(self, "let ExprKind::{kind} = {expr}.kind"); |
a2a8927a XL |
380 | macro_rules! kind { |
381 | ($($t:tt)*) => (kind(format_args!($($t)*))); | |
382 | } | |
383 | ||
384 | match expr.value.kind { | |
385 | ExprKind::Let(let_expr) => { | |
386 | bind!(self, let_expr); | |
387 | kind!("Let({let_expr})"); | |
388 | self.pat(field!(let_expr.pat)); | |
389 | // Does what ExprKind::Cast does, only adds a clause for the type | |
390 | // if it's a path | |
391 | if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { | |
392 | bind!(self, qpath); | |
2b03887a | 393 | chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); |
a2a8927a XL |
394 | self.qpath(qpath); |
395 | } | |
396 | self.expr(field!(let_expr.init)); | |
94222f64 | 397 | }, |
cdc7bbd5 | 398 | ExprKind::Box(inner) => { |
a2a8927a XL |
399 | bind!(self, inner); |
400 | kind!("Box({inner})"); | |
401 | self.expr(inner); | |
f20569fa | 402 | }, |
cdc7bbd5 | 403 | ExprKind::Array(elements) => { |
a2a8927a XL |
404 | bind!(self, elements); |
405 | kind!("Array({elements})"); | |
406 | self.slice(elements, |e| self.expr(e)); | |
f20569fa | 407 | }, |
cdc7bbd5 | 408 | ExprKind::Call(func, args) => { |
a2a8927a XL |
409 | bind!(self, func, args); |
410 | kind!("Call({func}, {args})"); | |
411 | self.expr(func); | |
412 | self.slice(args, |e| self.expr(e)); | |
f20569fa | 413 | }, |
f2b60f7d FG |
414 | ExprKind::MethodCall(method_name, receiver, args, _) => { |
415 | bind!(self, method_name, receiver, args); | |
416 | kind!("MethodCall({method_name}, {receiver}, {args}, _)"); | |
a2a8927a | 417 | self.ident(field!(method_name.ident)); |
f2b60f7d | 418 | self.expr(receiver); |
a2a8927a | 419 | self.slice(args, |e| self.expr(e)); |
f20569fa | 420 | }, |
cdc7bbd5 | 421 | ExprKind::Tup(elements) => { |
a2a8927a XL |
422 | bind!(self, elements); |
423 | kind!("Tup({elements})"); | |
424 | self.slice(elements, |e| self.expr(e)); | |
425 | }, | |
426 | ExprKind::Binary(op, left, right) => { | |
427 | bind!(self, op, left, right); | |
428 | kind!("Binary({op}, {left}, {right})"); | |
2b03887a | 429 | chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); |
a2a8927a XL |
430 | self.expr(left); |
431 | self.expr(right); | |
432 | }, | |
433 | ExprKind::Unary(op, inner) => { | |
434 | bind!(self, inner); | |
435 | kind!("Unary(UnOp::{op:?}, {inner})"); | |
436 | self.expr(inner); | |
f20569fa XL |
437 | }, |
438 | ExprKind::Lit(ref lit) => { | |
a2a8927a XL |
439 | bind!(self, lit); |
440 | kind!("Lit(ref {lit})"); | |
441 | self.lit(lit); | |
442 | }, | |
443 | ExprKind::Cast(expr, cast_ty) => { | |
444 | bind!(self, expr, cast_ty); | |
445 | kind!("Cast({expr}, {cast_ty})"); | |
446 | if let TyKind::Path(ref qpath) = cast_ty.value.kind { | |
447 | bind!(self, qpath); | |
2b03887a | 448 | chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind"); |
a2a8927a | 449 | self.qpath(qpath); |
f20569fa | 450 | } |
a2a8927a | 451 | self.expr(expr); |
f20569fa | 452 | }, |
cdc7bbd5 | 453 | ExprKind::Type(expr, _ty) => { |
a2a8927a XL |
454 | bind!(self, expr); |
455 | kind!("Type({expr}, _)"); | |
456 | self.expr(expr); | |
457 | }, | |
458 | ExprKind::Loop(body, label, des, _) => { | |
459 | bind!(self, body); | |
460 | opt_bind!(self, label); | |
461 | kind!("Loop({body}, {label}, LoopSource::{des:?}, _)"); | |
462 | self.block(body); | |
463 | label.if_some(|l| self.ident(field!(l.ident))); | |
464 | }, | |
465 | ExprKind::If(cond, then, else_expr) => { | |
466 | bind!(self, cond, then); | |
467 | opt_bind!(self, else_expr); | |
468 | kind!("If({cond}, {then}, {else_expr})"); | |
469 | self.expr(cond); | |
470 | self.expr(then); | |
471 | else_expr.if_some(|e| self.expr(e)); | |
472 | }, | |
473 | ExprKind::Match(scrutinee, arms, des) => { | |
474 | bind!(self, scrutinee, arms); | |
475 | kind!("Match({scrutinee}, {arms}, MatchSource::{des:?})"); | |
476 | self.expr(scrutinee); | |
477 | self.slice(arms, |arm| self.arm(arm)); | |
478 | }, | |
064997fb | 479 | ExprKind::Closure(&Closure { |
923072b8 FG |
480 | capture_clause, |
481 | fn_decl, | |
482 | body: body_id, | |
483 | movability, | |
484 | .. | |
064997fb | 485 | }) => { |
a2a8927a XL |
486 | let movability = OptionPat::new(movability.map(|m| format!("Movability::{m:?}"))); |
487 | ||
488 | let ret_ty = match fn_decl.output { | |
489 | FnRetTy::DefaultReturn(_) => "FnRetTy::DefaultReturn(_)", | |
490 | FnRetTy::Return(_) => "FnRetTy::Return(_ty)", | |
491 | }; | |
492 | ||
493 | bind!(self, fn_decl, body_id); | |
923072b8 | 494 | kind!("Closure(CaptureBy::{capture_clause:?}, {fn_decl}, {body_id}, _, {movability})"); |
2b03887a | 495 | chain!(self, "let {ret_ty} = {fn_decl}.output"); |
a2a8927a XL |
496 | self.body(body_id); |
497 | }, | |
498 | ExprKind::Yield(sub, source) => { | |
499 | bind!(self, sub); | |
500 | kind!("Yield(sub, YieldSource::{source:?})"); | |
501 | self.expr(sub); | |
502 | }, | |
503 | ExprKind::Block(block, label) => { | |
504 | bind!(self, block); | |
505 | opt_bind!(self, label); | |
506 | kind!("Block({block}, {label})"); | |
507 | self.block(block); | |
508 | label.if_some(|l| self.ident(field!(l.ident))); | |
f20569fa | 509 | }, |
cdc7bbd5 | 510 | ExprKind::Assign(target, value, _) => { |
a2a8927a XL |
511 | bind!(self, target, value); |
512 | kind!("Assign({target}, {value}, _span)"); | |
513 | self.expr(target); | |
514 | self.expr(value); | |
515 | }, | |
516 | ExprKind::AssignOp(op, target, value) => { | |
517 | bind!(self, op, target, value); | |
518 | kind!("AssignOp({op}, {target}, {value})"); | |
2b03887a | 519 | chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); |
a2a8927a XL |
520 | self.expr(target); |
521 | self.expr(value); | |
522 | }, | |
523 | ExprKind::Field(object, field_name) => { | |
524 | bind!(self, object, field_name); | |
525 | kind!("Field({object}, {field_name})"); | |
526 | self.ident(field_name); | |
527 | self.expr(object); | |
f20569fa | 528 | }, |
cdc7bbd5 | 529 | ExprKind::Index(object, index) => { |
a2a8927a XL |
530 | bind!(self, object, index); |
531 | kind!("Index({object}, {index})"); | |
532 | self.expr(object); | |
533 | self.expr(index); | |
534 | }, | |
535 | ExprKind::Path(ref qpath) => { | |
536 | bind!(self, qpath); | |
537 | kind!("Path(ref {qpath})"); | |
538 | self.qpath(qpath); | |
f20569fa | 539 | }, |
cdc7bbd5 | 540 | ExprKind::AddrOf(kind, mutability, inner) => { |
a2a8927a XL |
541 | bind!(self, inner); |
542 | kind!("AddrOf(BorrowKind::{kind:?}, Mutability::{mutability:?}, {inner})"); | |
543 | self.expr(inner); | |
544 | }, | |
545 | ExprKind::Break(destination, value) => { | |
546 | bind!(self, destination); | |
547 | opt_bind!(self, value); | |
548 | kind!("Break({destination}, {value})"); | |
549 | self.destination(destination); | |
550 | value.if_some(|e| self.expr(e)); | |
551 | }, | |
552 | ExprKind::Continue(destination) => { | |
553 | bind!(self, destination); | |
554 | kind!("Continue({destination})"); | |
555 | self.destination(destination); | |
556 | }, | |
557 | ExprKind::Ret(value) => { | |
558 | opt_bind!(self, value); | |
559 | kind!("Ret({value})"); | |
560 | value.if_some(|e| self.expr(e)); | |
f20569fa XL |
561 | }, |
562 | ExprKind::InlineAsm(_) => { | |
a2a8927a XL |
563 | kind!("InlineAsm(_)"); |
564 | out!("// unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment"); | |
f20569fa | 565 | }, |
a2a8927a XL |
566 | ExprKind::Struct(qpath, fields, base) => { |
567 | bind!(self, qpath, fields); | |
568 | opt_bind!(self, base); | |
569 | kind!("Struct({qpath}, {fields}, {base})"); | |
570 | self.qpath(qpath); | |
571 | self.slice(fields, |field| { | |
572 | self.ident(field!(field.ident)); | |
573 | self.expr(field!(field.expr)); | |
574 | }); | |
575 | base.if_some(|e| self.expr(e)); | |
576 | }, | |
577 | ExprKind::ConstBlock(_) => kind!("ConstBlock(_)"), | |
578 | ExprKind::Repeat(value, length) => { | |
579 | bind!(self, value, length); | |
580 | kind!("Repeat({value}, {length})"); | |
581 | self.expr(value); | |
582 | match length.value { | |
2b03887a | 583 | ArrayLen::Infer(..) => chain!(self, "let ArrayLen::Infer(..) = length"), |
a2a8927a XL |
584 | ArrayLen::Body(anon_const) => { |
585 | bind!(self, anon_const); | |
2b03887a | 586 | chain!(self, "let ArrayLen::Body({anon_const}) = {length}"); |
a2a8927a | 587 | self.body(field!(anon_const.body)); |
5099ac24 | 588 | }, |
f20569fa | 589 | } |
f20569fa | 590 | }, |
a2a8927a | 591 | ExprKind::Err => kind!("Err"), |
cdc7bbd5 | 592 | ExprKind::DropTemps(expr) => { |
a2a8927a XL |
593 | bind!(self, expr); |
594 | kind!("DropTemps({expr})"); | |
595 | self.expr(expr); | |
f20569fa XL |
596 | }, |
597 | } | |
598 | } | |
599 | ||
a2a8927a XL |
600 | fn block(&self, block: &Binding<&hir::Block<'_>>) { |
601 | self.slice(field!(block.stmts), |stmt| self.stmt(stmt)); | |
602 | self.option(field!(block.expr), "trailing_expr", |expr| { | |
603 | self.expr(expr); | |
604 | }); | |
f20569fa XL |
605 | } |
606 | ||
a2a8927a | 607 | fn body(&self, body_id: &Binding<hir::BodyId>) { |
f2b60f7d | 608 | let expr = self.cx.tcx.hir().body(body_id.value).value; |
a2a8927a | 609 | bind!(self, expr); |
2b03887a | 610 | chain!(self, "{expr} = &cx.tcx.hir().body({body_id}).value"); |
a2a8927a XL |
611 | self.expr(expr); |
612 | } | |
613 | ||
614 | fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { | |
2b03887a | 615 | let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); |
a2a8927a XL |
616 | macro_rules! kind { |
617 | ($($t:tt)*) => (kind(format_args!($($t)*))); | |
618 | } | |
619 | ||
620 | match pat.value.kind { | |
621 | PatKind::Wild => kind!("Wild"), | |
f2b60f7d | 622 | PatKind::Binding(ann, _, name, sub) => { |
a2a8927a XL |
623 | bind!(self, name); |
624 | opt_bind!(self, sub); | |
f2b60f7d FG |
625 | let ann = match ann { |
626 | BindingAnnotation::NONE => "NONE", | |
627 | BindingAnnotation::REF => "REF", | |
628 | BindingAnnotation::MUT => "MUT", | |
629 | BindingAnnotation::REF_MUT => "REF_MUT", | |
630 | }; | |
631 | kind!("Binding(BindingAnnotation::{ann}, _, {name}, {sub})"); | |
a2a8927a XL |
632 | self.ident(name); |
633 | sub.if_some(|p| self.pat(p)); | |
634 | }, | |
635 | PatKind::Struct(ref qpath, fields, ignore) => { | |
636 | bind!(self, qpath, fields); | |
637 | kind!("Struct(ref {qpath}, {fields}, {ignore})"); | |
638 | self.qpath(qpath); | |
639 | self.slice(fields, |field| { | |
640 | self.ident(field!(field.ident)); | |
641 | self.pat(field!(field.pat)); | |
642 | }); | |
f20569fa | 643 | }, |
cdc7bbd5 | 644 | PatKind::Or(fields) => { |
a2a8927a XL |
645 | bind!(self, fields); |
646 | kind!("Or({fields})"); | |
647 | self.slice(fields, |pat| self.pat(pat)); | |
648 | }, | |
649 | PatKind::TupleStruct(ref qpath, fields, skip_pos) => { | |
650 | bind!(self, qpath, fields); | |
651 | kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})"); | |
652 | self.qpath(qpath); | |
653 | self.slice(fields, |pat| self.pat(pat)); | |
654 | }, | |
655 | PatKind::Path(ref qpath) => { | |
656 | bind!(self, qpath); | |
657 | kind!("Path(ref {qpath})"); | |
658 | self.qpath(qpath); | |
f20569fa | 659 | }, |
cdc7bbd5 | 660 | PatKind::Tuple(fields, skip_pos) => { |
a2a8927a XL |
661 | bind!(self, fields); |
662 | kind!("Tuple({fields}, {skip_pos:?})"); | |
663 | self.slice(fields, |field| self.pat(field)); | |
f20569fa | 664 | }, |
cdc7bbd5 | 665 | PatKind::Box(pat) => { |
a2a8927a XL |
666 | bind!(self, pat); |
667 | kind!("Box({pat})"); | |
668 | self.pat(pat); | |
f20569fa | 669 | }, |
cdc7bbd5 | 670 | PatKind::Ref(pat, muta) => { |
a2a8927a XL |
671 | bind!(self, pat); |
672 | kind!("Ref({pat}, Mutability::{muta:?})"); | |
673 | self.pat(pat); | |
f20569fa | 674 | }, |
cdc7bbd5 | 675 | PatKind::Lit(lit_expr) => { |
a2a8927a XL |
676 | bind!(self, lit_expr); |
677 | kind!("Lit({lit_expr})"); | |
678 | self.expr(lit_expr); | |
679 | }, | |
680 | PatKind::Range(start, end, end_kind) => { | |
681 | opt_bind!(self, start, end); | |
682 | kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); | |
683 | start.if_some(|e| self.expr(e)); | |
684 | end.if_some(|e| self.expr(e)); | |
685 | }, | |
686 | PatKind::Slice(start, middle, end) => { | |
687 | bind!(self, start, end); | |
688 | opt_bind!(self, middle); | |
689 | kind!("Slice({start}, {middle}, {end})"); | |
690 | middle.if_some(|p| self.pat(p)); | |
691 | self.slice(start, |pat| self.pat(pat)); | |
692 | self.slice(end, |pat| self.pat(pat)); | |
f20569fa XL |
693 | }, |
694 | } | |
695 | } | |
696 | ||
a2a8927a | 697 | fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) { |
2b03887a | 698 | let kind = |kind| chain!(self, "let StmtKind::{kind} = {stmt}.kind"); |
a2a8927a XL |
699 | macro_rules! kind { |
700 | ($($t:tt)*) => (kind(format_args!($($t)*))); | |
701 | } | |
f20569fa | 702 | |
a2a8927a XL |
703 | match stmt.value.kind { |
704 | StmtKind::Local(local) => { | |
705 | bind!(self, local); | |
706 | kind!("Local({local})"); | |
707 | self.option(field!(local.init), "init", |init| { | |
708 | self.expr(init); | |
709 | }); | |
710 | self.pat(field!(local.pat)); | |
711 | }, | |
712 | StmtKind::Item(_) => kind!("Item(item_id)"), | |
cdc7bbd5 | 713 | StmtKind::Expr(e) => { |
a2a8927a XL |
714 | bind!(self, e); |
715 | kind!("Expr({e})"); | |
716 | self.expr(e); | |
f20569fa | 717 | }, |
cdc7bbd5 | 718 | StmtKind::Semi(e) => { |
a2a8927a XL |
719 | bind!(self, e); |
720 | kind!("Semi({e})"); | |
721 | self.expr(e); | |
f20569fa XL |
722 | }, |
723 | } | |
724 | } | |
f20569fa XL |
725 | } |
726 | ||
727 | fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { | |
728 | let attrs = cx.tcx.hir().attrs(hir_id); | |
729 | get_attr(cx.sess(), attrs, "author").count() > 0 | |
730 | } | |
731 | ||
a2a8927a XL |
732 | fn path_to_string(path: &QPath<'_>) -> String { |
733 | fn inner(s: &mut String, path: &QPath<'_>) { | |
734 | match *path { | |
735 | QPath::Resolved(_, path) => { | |
736 | for (i, segment) in path.segments.iter().enumerate() { | |
737 | if i > 0 { | |
738 | *s += ", "; | |
739 | } | |
740 | write!(s, "{:?}", segment.ident.as_str()).unwrap(); | |
f20569fa | 741 | } |
f20569fa | 742 | }, |
a2a8927a XL |
743 | QPath::TypeRelative(ty, segment) => match &ty.kind { |
744 | hir::TyKind::Path(inner_path) => { | |
745 | inner(s, inner_path); | |
746 | *s += ", "; | |
747 | write!(s, "{:?}", segment.ident.as_str()).unwrap(); | |
748 | }, | |
2b03887a | 749 | other => write!(s, "/* unimplemented: {other:?}*/").unwrap(), |
a2a8927a XL |
750 | }, |
751 | QPath::LangItem(..) => panic!("path_to_string: called for lang item qpath"), | |
752 | } | |
f20569fa | 753 | } |
a2a8927a XL |
754 | let mut s = String::new(); |
755 | inner(&mut s, path); | |
756 | s | |
f20569fa | 757 | } |