1 //! A group of attributes that can be attached to Rust code in order
2 //! to generate a clippy lint detecting said code automatically.
4 use clippy_utils::get_attr;
5 use rustc_ast::ast::{LitFloatType, LitKind};
6 use rustc_ast::walk_list;
7 use rustc_data_structures::fx::FxHashMap;
9 use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
10 use rustc_hir::{Block, Expr, ExprKind, Pat, PatKind, QPath, Stmt, StmtKind, TyKind};
11 use rustc_lint::{LateContext, LateLintPass, LintContext};
12 use rustc_middle::hir::map::Map;
13 use rustc_session::{declare_lint_pass, declare_tool_lint};
15 declare_clippy_lint! {
17 /// Generates clippy code that detects the offending pattern
21 /// // ./tests/ui/my_lint.rs
23 /// // detect the following pattern
26 /// // but ignore everything from here on
27 /// #![clippy::author = "ignore"]
33 /// Running `TESTNAME=ui/my_lint cargo uitest` will produce
34 /// a `./tests/ui/new_lint.stdout` file with the generated code:
37 /// // ./tests/ui/new_lint.stdout
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,
45 /// // report your lint here
51 "helper for writing lints"
54 declare_lint_pass!(Author => [LINT_AUTHOR]);
57 println!("if_chain! {{");
62 println!(" // report your lint here");
67 impl<'tcx> LateLintPass<'tcx> for Author {
68 fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
69 if !has_attr(cx, item.hir_id()) {
73 PrintVisitor::new("item").visit_item(item);
77 fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
78 if !has_attr(cx, item.hir_id()) {
82 PrintVisitor::new("item").visit_impl_item(item);
86 fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
87 if !has_attr(cx, item.hir_id()) {
91 PrintVisitor::new("item").visit_trait_item(item);
95 fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx hir::Variant<'_>) {
96 if !has_attr(cx, var.id) {
100 let parent_hir_id = cx.tcx.hir().get_parent_node(var.id);
101 PrintVisitor::new("var").visit_variant(var, &hir::Generics::empty(), parent_hir_id);
105 fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'_>) {
106 if !has_attr(cx, field.hir_id) {
110 PrintVisitor::new("field").visit_field_def(field);
114 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
115 if !has_attr(cx, expr.hir_id) {
119 PrintVisitor::new("expr").visit_expr(expr);
123 fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
124 if !has_attr(cx, arm.hir_id) {
128 PrintVisitor::new("arm").visit_arm(arm);
132 fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
133 if !has_attr(cx, stmt.hir_id) {
137 StmtKind::Expr(e) | StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return,
141 PrintVisitor::new("stmt").visit_stmt(stmt);
145 fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'_>) {
146 if !has_attr(cx, item.hir_id()) {
150 PrintVisitor::new("item").visit_foreign_item(item);
157 fn new(s: &'static str) -> Self {
159 ids: FxHashMap::default(),
160 current: s.to_owned(),
164 fn next(&mut self, s: &'static str) -> String {
165 use std::collections::hash_map::Entry::{Occupied, Vacant};
166 match self.ids.entry(s) {
167 // already there: start numbering from `1`
168 Occupied(mut occ) => {
169 let val = occ.get_mut();
171 format!("{}{}", s, *val)
173 // not there: insert and return name as given
181 fn print_qpath(&mut self, path: &QPath<'_>) {
182 if let QPath::LangItem(lang_item, _) = *path {
184 " if matches!({}, QPath::LangItem(LangItem::{:?}, _));",
185 self.current, lang_item,
188 print!(" if match_qpath({}, &[", self.current);
189 print_path(path, &mut true);
195 struct PrintVisitor {
196 /// Fields are the current index that needs to be appended to pattern
198 ids: FxHashMap<&'static str, usize>,
199 /// the name that needs to be destructured
203 impl<'tcx> Visitor<'tcx> for PrintVisitor {
204 type Map = Map<'tcx>;
206 #[allow(clippy::too_many_lines)]
207 fn visit_expr(&mut self, expr: &Expr<'_>) {
208 print!(" if let ExprKind::");
209 let current = format!("{}.kind", self.current);
211 ExprKind::Let(pat, expr, _) => {
212 let let_pat = self.next("pat");
213 let let_expr = self.next("expr");
214 println!(" Let(ref {}, ref {}, _) = {};", let_pat, let_expr, current);
215 self.current = let_expr;
216 self.visit_expr(expr);
217 self.current = let_pat;
220 ExprKind::Box(inner) => {
221 let inner_pat = self.next("inner");
222 println!("Box(ref {}) = {};", inner_pat, current);
223 self.current = inner_pat;
224 self.visit_expr(inner);
226 ExprKind::Array(elements) => {
227 let elements_pat = self.next("elements");
228 println!("Array(ref {}) = {};", elements_pat, current);
229 println!(" if {}.len() == {};", elements_pat, elements.len());
230 for (i, element) in elements.iter().enumerate() {
231 self.current = format!("{}[{}]", elements_pat, i);
232 self.visit_expr(element);
235 ExprKind::Call(func, args) => {
236 let func_pat = self.next("func");
237 let args_pat = self.next("args");
238 println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current);
239 self.current = func_pat;
240 self.visit_expr(func);
241 println!(" if {}.len() == {};", args_pat, args.len());
242 for (i, arg) in args.iter().enumerate() {
243 self.current = format!("{}[{}]", args_pat, i);
244 self.visit_expr(arg);
247 ExprKind::MethodCall(_method_name, ref _generics, _args, ref _fn_span) => {
249 "MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};",
252 println!(" // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment");
254 ExprKind::Tup(elements) => {
255 let elements_pat = self.next("elements");
256 println!("Tup(ref {}) = {};", elements_pat, current);
257 println!(" if {}.len() == {};", elements_pat, elements.len());
258 for (i, element) in elements.iter().enumerate() {
259 self.current = format!("{}[{}]", elements_pat, i);
260 self.visit_expr(element);
263 ExprKind::Binary(ref op, left, right) => {
264 let op_pat = self.next("op");
265 let left_pat = self.next("left");
266 let right_pat = self.next("right");
268 "Binary(ref {}, ref {}, ref {}) = {};",
269 op_pat, left_pat, right_pat, current
271 println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat);
272 self.current = left_pat;
273 self.visit_expr(left);
274 self.current = right_pat;
275 self.visit_expr(right);
277 ExprKind::Unary(ref op, inner) => {
278 let inner_pat = self.next("inner");
279 println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current);
280 self.current = inner_pat;
281 self.visit_expr(inner);
283 ExprKind::Lit(ref lit) => {
284 let lit_pat = self.next("lit");
285 println!("Lit(ref {}) = {};", lit_pat, current);
287 LitKind::Bool(val) => println!(" if let LitKind::Bool({:?}) = {}.node;", val, lit_pat),
288 LitKind::Char(c) => println!(" if let LitKind::Char({:?}) = {}.node;", c, lit_pat),
289 LitKind::Err(val) => println!(" if let LitKind::Err({}) = {}.node;", val, lit_pat),
290 LitKind::Byte(b) => println!(" if let LitKind::Byte({}) = {}.node;", b, lit_pat),
291 // FIXME: also check int type
292 LitKind::Int(i, _) => println!(" if let LitKind::Int({}, _) = {}.node;", i, lit_pat),
293 LitKind::Float(_, LitFloatType::Suffixed(_)) => println!(
294 " if let LitKind::Float(_, LitFloatType::Suffixed(_)) = {}.node;",
297 LitKind::Float(_, LitFloatType::Unsuffixed) => println!(
298 " if let LitKind::Float(_, LitFloatType::Unsuffixed) = {}.node;",
301 LitKind::ByteStr(ref vec) => {
302 let vec_pat = self.next("vec");
303 println!(" if let LitKind::ByteStr(ref {}) = {}.node;", vec_pat, lit_pat);
304 println!(" if let [{:?}] = **{};", vec, vec_pat);
306 LitKind::Str(ref text, _) => {
307 let str_pat = self.next("s");
308 println!(" if let LitKind::Str(ref {}, _) = {}.node;", str_pat, lit_pat);
309 println!(" if {}.as_str() == {:?}", str_pat, &*text.as_str());
313 ExprKind::Cast(expr, ty) => {
314 let cast_pat = self.next("expr");
315 let cast_ty = self.next("cast_ty");
316 let qp_label = self.next("qp");
318 println!("Cast(ref {}, ref {}) = {};", cast_pat, cast_ty, current);
319 if let TyKind::Path(ref qp) = ty.kind {
320 println!(" if let TyKind::Path(ref {}) = {}.kind;", qp_label, cast_ty);
321 self.current = qp_label;
322 self.print_qpath(qp);
324 self.current = cast_pat;
325 self.visit_expr(expr);
327 ExprKind::Type(expr, _ty) => {
328 let cast_pat = self.next("expr");
329 println!("Type(ref {}, _) = {};", cast_pat, current);
330 self.current = cast_pat;
331 self.visit_expr(expr);
333 ExprKind::Loop(body, _, des, _) => {
334 let body_pat = self.next("body");
335 let label_pat = self.next("label");
337 "Loop(ref {}, ref {}, LoopSource::{:?}) = {};",
338 body_pat, label_pat, des, current
340 self.current = body_pat;
341 self.visit_block(body);
343 ExprKind::If(cond, then, ref opt_else) => {
344 let cond_pat = self.next("cond");
345 let then_pat = self.next("then");
346 if let Some(else_) = *opt_else {
347 let else_pat = self.next("else_");
349 "If(ref {}, ref {}, Some(ref {})) = {};",
350 cond_pat, then_pat, else_pat, current
352 self.current = else_pat;
353 self.visit_expr(else_);
355 println!("If(ref {}, ref {}, None) = {};", cond_pat, then_pat, current);
357 self.current = cond_pat;
358 self.visit_expr(cond);
359 self.current = then_pat;
360 self.visit_expr(then);
362 ExprKind::Match(expr, arms, des) => {
363 let expr_pat = self.next("expr");
364 let arms_pat = self.next("arms");
366 "Match(ref {}, ref {}, MatchSource::{:?}) = {};",
367 expr_pat, arms_pat, des, current
369 self.current = expr_pat;
370 self.visit_expr(expr);
371 println!(" if {}.len() == {};", arms_pat, arms.len());
372 for (i, arm) in arms.iter().enumerate() {
373 self.current = format!("{}[{}].body", arms_pat, i);
374 self.visit_expr(arm.body);
375 if let Some(ref guard) = arm.guard {
376 let guard_pat = self.next("guard");
377 println!(" if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i);
379 hir::Guard::If(if_expr) => {
380 let if_expr_pat = self.next("expr");
381 println!(" if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat);
382 self.current = if_expr_pat;
383 self.visit_expr(if_expr);
385 hir::Guard::IfLet(if_let_pat, if_let_expr) => {
386 let if_let_pat_pat = self.next("pat");
387 let if_let_expr_pat = self.next("expr");
389 " if let Guard::IfLet(ref {}, ref {}) = {};",
390 if_let_pat_pat, if_let_expr_pat, guard_pat
392 self.current = if_let_expr_pat;
393 self.visit_expr(if_let_expr);
394 self.current = if_let_pat_pat;
395 self.visit_pat(if_let_pat);
399 self.current = format!("{}[{}].pat", arms_pat, i);
400 self.visit_pat(arm.pat);
403 ExprKind::Closure(ref _capture_clause, _func, _, _, _) => {
404 println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current);
405 println!(" // unimplemented: `ExprKind::Closure` is not further destructured at the moment");
407 ExprKind::Yield(sub, _) => {
408 let sub_pat = self.next("sub");
409 println!("Yield(ref sub) = {};", current);
410 self.current = sub_pat;
411 self.visit_expr(sub);
413 ExprKind::Block(block, _) => {
414 let block_pat = self.next("block");
415 println!("Block(ref {}) = {};", block_pat, current);
416 self.current = block_pat;
417 self.visit_block(block);
419 ExprKind::Assign(target, value, _) => {
420 let target_pat = self.next("target");
421 let value_pat = self.next("value");
423 "Assign(ref {}, ref {}, ref _span) = {};",
424 target_pat, value_pat, current
426 self.current = target_pat;
427 self.visit_expr(target);
428 self.current = value_pat;
429 self.visit_expr(value);
431 ExprKind::AssignOp(ref op, target, value) => {
432 let op_pat = self.next("op");
433 let target_pat = self.next("target");
434 let value_pat = self.next("value");
436 "AssignOp(ref {}, ref {}, ref {}) = {};",
437 op_pat, target_pat, value_pat, current
439 println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat);
440 self.current = target_pat;
441 self.visit_expr(target);
442 self.current = value_pat;
443 self.visit_expr(value);
445 ExprKind::Field(object, ref field_ident) => {
446 let obj_pat = self.next("object");
447 let field_name_pat = self.next("field_name");
448 println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current);
449 println!(" if {}.as_str() == {:?}", field_name_pat, field_ident.as_str());
450 self.current = obj_pat;
451 self.visit_expr(object);
453 ExprKind::Index(object, index) => {
454 let object_pat = self.next("object");
455 let index_pat = self.next("index");
456 println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current);
457 self.current = object_pat;
458 self.visit_expr(object);
459 self.current = index_pat;
460 self.visit_expr(index);
462 ExprKind::Path(ref path) => {
463 let path_pat = self.next("path");
464 println!("Path(ref {}) = {};", path_pat, current);
465 self.current = path_pat;
466 self.print_qpath(path);
468 ExprKind::AddrOf(kind, mutability, inner) => {
469 let inner_pat = self.next("inner");
471 "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};",
472 kind, mutability, inner_pat, current
474 self.current = inner_pat;
475 self.visit_expr(inner);
477 ExprKind::Break(ref _destination, ref opt_value) => {
478 let destination_pat = self.next("destination");
479 if let Some(value) = *opt_value {
480 let value_pat = self.next("value");
481 println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current);
482 self.current = value_pat;
483 self.visit_expr(value);
485 println!("Break(ref {}, None) = {};", destination_pat, current);
487 // FIXME: implement label printing
489 ExprKind::Continue(ref _destination) => {
490 let destination_pat = self.next("destination");
491 println!("Again(ref {}) = {};", destination_pat, current);
492 // FIXME: implement label printing
494 ExprKind::Ret(ref opt_value) => {
495 if let Some(value) = *opt_value {
496 let value_pat = self.next("value");
497 println!("Ret(Some(ref {})) = {};", value_pat, current);
498 self.current = value_pat;
499 self.visit_expr(value);
501 println!("Ret(None) = {};", current);
504 ExprKind::InlineAsm(_) => {
505 println!("InlineAsm(_) = {};", current);
506 println!(" // unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
508 ExprKind::LlvmInlineAsm(_) => {
509 println!("LlvmInlineAsm(_) = {};", current);
510 println!(" // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment");
512 ExprKind::Struct(path, fields, ref opt_base) => {
513 let path_pat = self.next("path");
514 let fields_pat = self.next("fields");
515 if let Some(base) = *opt_base {
516 let base_pat = self.next("base");
518 "Struct(ref {}, ref {}, Some(ref {})) = {};",
519 path_pat, fields_pat, base_pat, current
521 self.current = base_pat;
522 self.visit_expr(base);
524 println!("Struct(ref {}, ref {}, None) = {};", path_pat, fields_pat, current);
526 self.current = path_pat;
527 self.print_qpath(path);
528 println!(" if {}.len() == {};", fields_pat, fields.len());
529 println!(" // unimplemented: field checks");
531 ExprKind::ConstBlock(_) => {
532 let value_pat = self.next("value");
533 println!("Const({})", value_pat);
534 self.current = value_pat;
536 // FIXME: compute length (needs type info)
537 ExprKind::Repeat(value, _) => {
538 let value_pat = self.next("value");
539 println!("Repeat(ref {}, _) = {};", value_pat, current);
540 println!("// unimplemented: repeat count check");
541 self.current = value_pat;
542 self.visit_expr(value);
545 println!("Err = {}", current);
547 ExprKind::DropTemps(expr) => {
548 let expr_pat = self.next("expr");
549 println!("DropTemps(ref {}) = {};", expr_pat, current);
550 self.current = expr_pat;
551 self.visit_expr(expr);
556 fn visit_block(&mut self, block: &Block<'_>) {
557 println!(" if {}.stmts.len() == {};", self.current, block.stmts.len());
558 let block_name = self.current.clone();
559 for (i, stmt) in block.stmts.iter().enumerate() {
560 self.current = format!("{}.stmts[{}]", block_name, i);
561 self.visit_stmt(stmt);
563 if let Some(expr) = block.expr {
564 self.current = self.next("trailing_expr");
565 println!(" if let Some({}) = &{}.expr;", self.current, block_name);
566 self.visit_expr(expr);
568 println!(" if {}.expr.is_none();", block_name);
572 #[allow(clippy::too_many_lines)]
573 fn visit_pat(&mut self, pat: &Pat<'_>) {
574 print!(" if let PatKind::");
575 let current = format!("{}.kind", self.current);
577 PatKind::Wild => println!("Wild = {};", current),
578 PatKind::Binding(anno, .., ident, ref sub) => {
579 let anno_pat = &format!("BindingAnnotation::{:?}", anno);
580 let name_pat = self.next("name");
581 if let Some(sub) = *sub {
582 let sub_pat = self.next("sub");
584 "Binding({}, _, {}, Some(ref {})) = {};",
585 anno_pat, name_pat, sub_pat, current
587 self.current = sub_pat;
590 println!("Binding({}, _, {}, None) = {};", anno_pat, name_pat, current);
592 println!(" if {}.as_str() == \"{}\";", name_pat, ident.as_str());
594 PatKind::Struct(ref path, fields, ignore) => {
595 let path_pat = self.next("path");
596 let fields_pat = self.next("fields");
598 "Struct(ref {}, ref {}, {}) = {};",
599 path_pat, fields_pat, ignore, current
601 self.current = path_pat;
602 self.print_qpath(path);
603 println!(" if {}.len() == {};", fields_pat, fields.len());
604 println!(" // unimplemented: field checks");
606 PatKind::Or(fields) => {
607 let fields_pat = self.next("fields");
608 println!("Or(ref {}) = {};", fields_pat, current);
609 println!(" if {}.len() == {};", fields_pat, fields.len());
610 println!(" // unimplemented: field checks");
612 PatKind::TupleStruct(ref path, fields, skip_pos) => {
613 let path_pat = self.next("path");
614 let fields_pat = self.next("fields");
616 "TupleStruct(ref {}, ref {}, {:?}) = {};",
617 path_pat, fields_pat, skip_pos, current
619 self.current = path_pat;
620 self.print_qpath(path);
621 println!(" if {}.len() == {};", fields_pat, fields.len());
622 println!(" // unimplemented: field checks");
624 PatKind::Path(ref path) => {
625 let path_pat = self.next("path");
626 println!("Path(ref {}) = {};", path_pat, current);
627 self.current = path_pat;
628 self.print_qpath(path);
630 PatKind::Tuple(fields, skip_pos) => {
631 let fields_pat = self.next("fields");
632 println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current);
633 println!(" if {}.len() == {};", fields_pat, fields.len());
634 println!(" // unimplemented: field checks");
636 PatKind::Box(pat) => {
637 let pat_pat = self.next("pat");
638 println!("Box(ref {}) = {};", pat_pat, current);
639 self.current = pat_pat;
642 PatKind::Ref(pat, muta) => {
643 let pat_pat = self.next("pat");
644 println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current);
645 self.current = pat_pat;
648 PatKind::Lit(lit_expr) => {
649 let lit_expr_pat = self.next("lit_expr");
650 println!("Lit(ref {}) = {}", lit_expr_pat, current);
651 self.current = lit_expr_pat;
652 self.visit_expr(lit_expr);
654 PatKind::Range(ref start, ref end, end_kind) => {
655 let start_pat = self.next("start");
656 let end_pat = self.next("end");
658 "Range(ref {}, ref {}, RangeEnd::{:?}) = {};",
659 start_pat, end_pat, end_kind, current
661 self.current = start_pat;
662 walk_list!(self, visit_expr, start);
663 self.current = end_pat;
664 walk_list!(self, visit_expr, end);
666 PatKind::Slice(start, ref middle, end) => {
667 let start_pat = self.next("start");
668 let end_pat = self.next("end");
669 if let Some(middle) = middle {
670 let middle_pat = self.next("middle");
672 "Slice(ref {}, Some(ref {}), ref {}) = {};",
673 start_pat, middle_pat, end_pat, current
675 self.current = middle_pat;
676 self.visit_pat(middle);
678 println!("Slice(ref {}, None, ref {}) = {};", start_pat, end_pat, current);
680 println!(" if {}.len() == {};", start_pat, start.len());
681 for (i, pat) in start.iter().enumerate() {
682 self.current = format!("{}[{}]", start_pat, i);
685 println!(" if {}.len() == {};", end_pat, end.len());
686 for (i, pat) in end.iter().enumerate() {
687 self.current = format!("{}[{}]", end_pat, i);
694 fn visit_stmt(&mut self, s: &Stmt<'_>) {
695 print!(" if let StmtKind::");
696 let current = format!("{}.kind", self.current);
698 // A local (let) binding:
699 StmtKind::Local(local) => {
700 let local_pat = self.next("local");
701 println!("Local(ref {}) = {};", local_pat, current);
702 if let Some(init) = local.init {
703 let init_pat = self.next("init");
704 println!(" if let Some(ref {}) = {}.init;", init_pat, local_pat);
705 self.current = init_pat;
706 self.visit_expr(init);
708 self.current = format!("{}.pat", local_pat);
709 self.visit_pat(local.pat);
712 StmtKind::Item(_) => {
713 println!("Item(item_id) = {};", current);
716 // Expr without trailing semi-colon (must have unit type):
717 StmtKind::Expr(e) => {
718 let e_pat = self.next("e");
719 println!("Expr(ref {}, _) = {}", e_pat, current);
720 self.current = e_pat;
724 // Expr with trailing semi-colon (may have any type):
725 StmtKind::Semi(e) => {
726 let e_pat = self.next("e");
727 println!("Semi(ref {}, _) = {}", e_pat, current);
728 self.current = e_pat;
734 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
735 NestedVisitorMap::None
739 fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
740 let attrs = cx.tcx.hir().attrs(hir_id);
741 get_attr(cx.sess(), attrs, "author").count() > 0
744 fn print_path(path: &QPath<'_>, first: &mut bool) {
746 QPath::Resolved(_, path) => {
747 for segment in path.segments {
753 print!("{:?}", segment.ident.as_str());
756 QPath::TypeRelative(ty, segment) => match ty.kind {
757 hir::TyKind::Path(ref inner_path) => {
758 print_path(inner_path, first);
764 print!("{:?}", segment.ident.as_str());
766 ref other => print!("/* unimplemented: {:?}*/", other),
768 QPath::LangItem(..) => panic!("print_path: called for lang item qpath"),