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