Auto merge of #8701 - xFrednet:0000-clippy-print-hir-attr, r=flip1995

Rework `#[clippy::dump]` attribute for debugging

Hey `@rust-lang/clippy,` this adds a new `#[clippy::print_hir]` attribute that prints the node to the console using `{:#?}`. Personally, I use print debugging quite a lot while working on Clippy, and this is a simple shortcut that also works in the playground (Once this has been synced). The question is now, if we want to have this attribute. Are there any concerns? I think it's similar to our `#[clippy::author]` attribute.

I haven't added a test, as the `.stdout` file would require updates with every HIR change inside rustc. Here are some examples, for the current implementation

<details>
<summary>`do_something(&map);`</summary>

```rs
Expr {
    hir_id: HirId {
        owner: DefId(0:7 ~ aaa[995b]::main),
        local_id: 21,
    },
    kind: Call(
        Expr {
            hir_id: HirId {
                owner: DefId(0:7 ~ aaa[995b]::main),
                local_id: 17,
            },
            kind: Path(
                Resolved(
                    None,
                    Path {
                        span: tests/ui/aaa.rs:23:5: 23:17 (#0),
                        res: Def(
                            Fn,
                            DefId(0:6 ~ aaa[995b]::do_something),
                        ),
                        segments: [
                            PathSegment {
                                ident: do_something#0,
                                hir_id: Some(
                                    HirId {
                                        owner: DefId(0:7 ~ aaa[995b]::main),
                                        local_id: 16,
                                    },
                                ),
                                res: Some(
                                    Err,
                                ),
                                args: None,
                                infer_args: true,
                            },
                        ],
                    },
                ),
            ),
            span: tests/ui/aaa.rs:23:5: 23:17 (#0),
        },
        [
            Expr {
                hir_id: HirId {
                    owner: DefId(0:7 ~ aaa[995b]::main),
                    local_id: 20,
                },
                kind: AddrOf(
                    Ref,
                    Not,
                    Expr {
                        hir_id: HirId {
                            owner: DefId(0:7 ~ aaa[995b]::main),
                            local_id: 19,
                        },
                        kind: Path(
                            Resolved(
                                None,
                                Path {
                                    span: tests/ui/aaa.rs:23:19: 23:22 (#0),
                                    res: Local(
                                        HirId {
                                            owner: DefId(0:7 ~ aaa[995b]::main),
                                            local_id: 15,
                                        },
                                    ),
                                    segments: [
                                        PathSegment {
                                            ident: map#0,
                                            hir_id: Some(
                                                HirId {
                                                    owner: DefId(0:7 ~ aaa[995b]::main),
                                                    local_id: 18,
                                                },
                                            ),
                                            res: Some(
                                                Local(
                                                    HirId {
                                                        owner: DefId(0:7 ~ aaa[995b]::main),
                                                        local_id: 15,
                                                    },
                                                ),
                                            ),
                                            args: None,
                                            infer_args: true,
                                        },
                                    ],
                                },
                            ),
                        ),
                        span: tests/ui/aaa.rs:23:19: 23:22 (#0),
                    },
                ),
                span: tests/ui/aaa.rs:23:18: 23:22 (#0),
            },
        ],
    ),
    span: tests/ui/aaa.rs:23:5: 23:23 (#0),
}
```
</details>

<details>
<summary>`use std::collections::HashMap;`</summary>

```rs
Item {
    ident: HashMap#0,
    def_id: DefId(0:5 ~ aaa[995b]::{misc#1}),
    kind: Use(
        Path {
            span: tests/ui/aaa.rs:8:5: 8:30 (#0),
            res: Def(
                Struct,
                DefId(1:1294 ~ std[928b]::collections:#️⃣:map::HashMap),
            ),
            segments: [
                PathSegment {
                    ident: std#0,
                    hir_id: Some(
                        HirId {
                            owner: DefId(0:5 ~ aaa[995b]::{misc#1}),
                            local_id: 1,
                        },
                    ),
                    res: Some(
                        Def(
                            Mod,
                            DefId(1:0 ~ std[928b]),
                        ),
                    ),
                    args: None,
                    infer_args: false,
                },
                PathSegment {
                    ident: collections#0,
                    hir_id: Some(
                        HirId {
                            owner: DefId(0:5 ~ aaa[995b]::{misc#1}),
                            local_id: 2,
                        },
                    ),
                    res: Some(
                        Def(
                            Mod,
                            DefId(1:1193 ~ std[928b]::collections),
                        ),
                    ),
                    args: None,
                    infer_args: false,
                },
                PathSegment {
                    ident: HashMap#0,
                    hir_id: Some(
                        HirId {
                            owner: DefId(0:5 ~ aaa[995b]::{misc#1}),
                            local_id: 3,
                        },
                    ),
                    res: Some(
                        Err,
                    ),
                    args: None,
                    infer_args: false,
                },
            ],
        },
        Single,
    ),
    vis: Spanned {
        node: Inherited,
        span: tests/ui/aaa.rs:8:1: 8:1 (#0),
    },
    span: tests/ui/aaa.rs:8:1: 8:31 (#0),
}
```

</details>

<details>
<summary>`"100"`</summary>

```rs
Expr {
    hir_id: HirId {
        owner: DefId(0:7 ~ aaa[995b]::main),
        local_id: 27,
    },
    kind: Lit(
        Spanned {
            node: Str(
                "100",
                Cooked,
            ),
            span: tests/ui/aaa.rs:28:9: 28:14 (#0),
        },
    ),
    span: tests/ui/aaa.rs:28:9: 28:14 (#0),
}
```
</details>

---

changelog: Added `[clippy::print_hir]` to inspect rustc's internal representation
This commit is contained in:
bors 2022-04-18 13:06:03 +00:00
commit 95de4dcb5e
6 changed files with 72 additions and 558 deletions

View File

@ -502,7 +502,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
{ {
store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
store.register_late_pass(|| Box::new(utils::inspector::DeepCodeInspector));
store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls)); store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls));
store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new())); store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle)); store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle));
@ -514,6 +513,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl)); store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
} }
store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir));
store.register_late_pass(|| Box::new(utils::author::Author)); store.register_late_pass(|| Box::new(utils::author::Author));
store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding)); store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding));
store.register_late_pass(|| Box::new(serde_api::SerdeApi)); store.register_late_pass(|| Box::new(serde_api::SerdeApi));

View File

@ -0,0 +1,55 @@
use clippy_utils::get_attr;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// It formats the attached node with `{:#?}` and writes the result to the
/// standard output. This is intended for debugging.
///
/// ### Examples
/// ```rs
/// #[clippy::dump]
/// use std::mem;
///
/// #[clippy::dump]
/// fn foo(input: u32) -> u64 {
/// input as u64
/// }
/// ```
pub DUMP_HIR,
internal_warn,
"helper to dump info about code"
}
declare_lint_pass!(DumpHir => [DUMP_HIR]);
impl<'tcx> LateLintPass<'tcx> for DumpHir {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
if has_attr(cx, item.hir_id()) {
println!("{item:#?}");
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if has_attr(cx, expr.hir_id) {
println!("{expr:#?}");
}
}
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
match stmt.kind {
hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return,
_ => {},
}
if has_attr(cx, stmt.hir_id) {
println!("{stmt:#?}");
}
}
}
fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
let attrs = cx.tcx.hir().attrs(hir_id);
get_attr(cx.sess(), attrs, "dump").count() > 0
}

View File

@ -1,555 +0,0 @@
//! checks for attributes
use clippy_utils::get_attr;
use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::Session;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Dumps every ast/hir node which has the `#[clippy::dump]`
/// attribute
///
/// ### Example
/// ```rust,ignore
/// #[clippy::dump]
/// extern crate foo;
/// ```
///
/// prints
///
/// ```text
/// item `foo`
/// visibility inherited from outer item
/// extern crate dylib source: "/path/to/foo.so"
/// ```
pub DEEP_CODE_INSPECTION,
internal_warn,
"helper to dump info about code"
}
declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]);
impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) {
return;
}
print_item(cx, item);
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) {
return;
}
println!("impl item `{}`", item.ident.name);
match item.vis.node {
hir::VisibilityKind::Public => println!("public"),
hir::VisibilityKind::Crate(_) => println!("visible crate wide"),
hir::VisibilityKind::Restricted { path, .. } => println!(
"visible in module `{}`",
rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false))
),
hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
}
match item.kind {
hir::ImplItemKind::Const(_, body_id) => {
println!("associated constant");
print_expr(cx, &cx.tcx.hir().body(body_id).value, 1);
},
hir::ImplItemKind::Fn(..) => println!("method"),
hir::ImplItemKind::TyAlias(_) => println!("associated type"),
}
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) {
return;
}
print_expr(cx, expr, 0);
}
fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
if !has_attr(cx.sess(), cx.tcx.hir().attrs(arm.hir_id)) {
return;
}
print_pat(cx, arm.pat, 1);
if let Some(ref guard) = arm.guard {
println!("guard:");
print_guard(cx, guard, 1);
}
println!("body:");
print_expr(cx, arm.body, 1);
}
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
if !has_attr(cx.sess(), cx.tcx.hir().attrs(stmt.hir_id)) {
return;
}
match stmt.kind {
hir::StmtKind::Local(local) => {
println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id));
println!("pattern:");
print_pat(cx, local.pat, 0);
if let Some(e) = local.init {
println!("init expression:");
print_expr(cx, e, 0);
}
},
hir::StmtKind::Item(_) => println!("item decl"),
hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0),
}
}
}
fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
get_attr(sess, attrs, "dump").count() > 0
}
#[allow(clippy::similar_names)]
#[allow(clippy::too_many_lines)]
fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
let ind = " ".repeat(indent);
println!("{}+", ind);
println!("{}ty: {}", ind, cx.typeck_results().expr_ty(expr));
println!(
"{}adjustments: {:?}",
ind,
cx.typeck_results().adjustments().get(expr.hir_id)
);
match expr.kind {
hir::ExprKind::Box(e) => {
println!("{}Box", ind);
print_expr(cx, e, indent + 1);
},
hir::ExprKind::Array(v) => {
println!("{}Array", ind);
for e in v {
print_expr(cx, e, indent + 1);
}
},
hir::ExprKind::Call(func, args) => {
println!("{}Call", ind);
println!("{}function:", ind);
print_expr(cx, func, indent + 1);
println!("{}arguments:", ind);
for arg in args {
print_expr(cx, arg, indent + 1);
}
},
hir::ExprKind::Let(hir::Let { pat, init, ty, .. }) => {
print_pat(cx, pat, indent + 1);
if let Some(ty) = ty {
println!("{} type annotation: {:?}", ind, ty);
}
print_expr(cx, init, indent + 1);
},
hir::ExprKind::MethodCall(path, args, _) => {
println!("{}MethodCall", ind);
println!("{}method name: {}", ind, path.ident.name);
for arg in args {
print_expr(cx, arg, indent + 1);
}
},
hir::ExprKind::Tup(v) => {
println!("{}Tup", ind);
for e in v {
print_expr(cx, e, indent + 1);
}
},
hir::ExprKind::Binary(op, lhs, rhs) => {
println!("{}Binary", ind);
println!("{}op: {:?}", ind, op.node);
println!("{}lhs:", ind);
print_expr(cx, lhs, indent + 1);
println!("{}rhs:", ind);
print_expr(cx, rhs, indent + 1);
},
hir::ExprKind::Unary(op, inner) => {
println!("{}Unary", ind);
println!("{}op: {:?}", ind, op);
print_expr(cx, inner, indent + 1);
},
hir::ExprKind::Lit(ref lit) => {
println!("{}Lit", ind);
println!("{}{:?}", ind, lit);
},
hir::ExprKind::Cast(e, target) => {
println!("{}Cast", ind);
print_expr(cx, e, indent + 1);
println!("{}target type: {:?}", ind, target);
},
hir::ExprKind::Type(e, target) => {
println!("{}Type", ind);
print_expr(cx, e, indent + 1);
println!("{}target type: {:?}", ind, target);
},
hir::ExprKind::Loop(..) => {
println!("{}Loop", ind);
},
hir::ExprKind::If(cond, _, ref else_opt) => {
println!("{}If", ind);
println!("{}condition:", ind);
print_expr(cx, cond, indent + 1);
if let Some(els) = *else_opt {
println!("{}else:", ind);
print_expr(cx, els, indent + 1);
}
},
hir::ExprKind::Match(cond, _, ref source) => {
println!("{}Match", ind);
println!("{}condition:", ind);
print_expr(cx, cond, indent + 1);
println!("{}source: {:?}", ind, source);
},
hir::ExprKind::Closure(ref clause, _, _, _, _) => {
println!("{}Closure", ind);
println!("{}clause: {:?}", ind, clause);
},
hir::ExprKind::Yield(sub, _) => {
println!("{}Yield", ind);
print_expr(cx, sub, indent + 1);
},
hir::ExprKind::Block(_, _) => {
println!("{}Block", ind);
},
hir::ExprKind::Assign(lhs, rhs, _) => {
println!("{}Assign", ind);
println!("{}lhs:", ind);
print_expr(cx, lhs, indent + 1);
println!("{}rhs:", ind);
print_expr(cx, rhs, indent + 1);
},
hir::ExprKind::AssignOp(ref binop, lhs, rhs) => {
println!("{}AssignOp", ind);
println!("{}op: {:?}", ind, binop.node);
println!("{}lhs:", ind);
print_expr(cx, lhs, indent + 1);
println!("{}rhs:", ind);
print_expr(cx, rhs, indent + 1);
},
hir::ExprKind::Field(e, ident) => {
println!("{}Field", ind);
println!("{}field name: {}", ind, ident.name);
println!("{}struct expr:", ind);
print_expr(cx, e, indent + 1);
},
hir::ExprKind::Index(arr, idx) => {
println!("{}Index", ind);
println!("{}array expr:", ind);
print_expr(cx, arr, indent + 1);
println!("{}index expr:", ind);
print_expr(cx, idx, indent + 1);
},
hir::ExprKind::Path(hir::QPath::Resolved(ref ty, path)) => {
println!("{}Resolved Path, {:?}", ind, ty);
println!("{}path: {:?}", ind, path);
},
hir::ExprKind::Path(hir::QPath::TypeRelative(ty, seg)) => {
println!("{}Relative Path, {:?}", ind, ty);
println!("{}seg: {:?}", ind, seg);
},
hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => {
println!("{}Lang Item Path, {:?}", ind, lang_item.name());
},
hir::ExprKind::AddrOf(kind, ref muta, e) => {
println!("{}AddrOf", ind);
println!("kind: {:?}", kind);
println!("mutability: {:?}", muta);
print_expr(cx, e, indent + 1);
},
hir::ExprKind::Break(_, ref e) => {
println!("{}Break", ind);
if let Some(e) = *e {
print_expr(cx, e, indent + 1);
}
},
hir::ExprKind::Continue(_) => println!("{}Again", ind),
hir::ExprKind::Ret(ref e) => {
println!("{}Ret", ind);
if let Some(e) = *e {
print_expr(cx, e, indent + 1);
}
},
hir::ExprKind::InlineAsm(asm) => {
println!("{}InlineAsm", ind);
println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template));
println!("{}options: {:?}", ind, asm.options);
println!("{}operands:", ind);
for (op, _op_sp) in asm.operands {
match op {
hir::InlineAsmOperand::In { expr, .. }
| hir::InlineAsmOperand::InOut { expr, .. }
| hir::InlineAsmOperand::Sym { expr } => print_expr(cx, expr, indent + 1),
hir::InlineAsmOperand::Out { expr, .. } => {
if let Some(expr) = expr {
print_expr(cx, expr, indent + 1);
}
},
hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
print_expr(cx, in_expr, indent + 1);
if let Some(out_expr) = out_expr {
print_expr(cx, out_expr, indent + 1);
}
},
hir::InlineAsmOperand::Const { anon_const } => {
println!("{}anon_const:", ind);
print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
},
}
}
},
hir::ExprKind::Struct(path, fields, ref base) => {
println!("{}Struct", ind);
println!("{}path: {:?}", ind, path);
for field in fields {
println!("{}field \"{}\":", ind, field.ident.name);
print_expr(cx, field.expr, indent + 1);
}
if let Some(base) = *base {
println!("{}base:", ind);
print_expr(cx, base, indent + 1);
}
},
hir::ExprKind::ConstBlock(ref anon_const) => {
println!("{}ConstBlock", ind);
println!("{}anon_const:", ind);
print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
},
hir::ExprKind::Repeat(val, length) => {
println!("{}Repeat", ind);
println!("{}value:", ind);
print_expr(cx, val, indent + 1);
println!("{}repeat count:", ind);
match length {
hir::ArrayLen::Infer(_, _) => println!("{}repeat count: _", ind),
hir::ArrayLen::Body(anon_const) => {
print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1);
},
}
},
hir::ExprKind::Err => {
println!("{}Err", ind);
},
hir::ExprKind::DropTemps(e) => {
println!("{}DropTemps", ind);
print_expr(cx, e, indent + 1);
},
}
}
fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
let did = item.def_id;
println!("item `{}`", item.ident.name);
match item.vis.node {
hir::VisibilityKind::Public => println!("public"),
hir::VisibilityKind::Crate(_) => println!("visible crate wide"),
hir::VisibilityKind::Restricted { path, .. } => println!(
"visible in module `{}`",
rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false))
),
hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
}
match item.kind {
hir::ItemKind::ExternCrate(ref _renamed_from) => {
if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(did) {
let source = cx.tcx.used_crate_source(crate_id);
if let Some(ref src) = source.dylib {
println!("extern crate dylib source: {:?}", src.0);
}
if let Some(ref src) = source.rlib {
println!("extern crate rlib source: {:?}", src.0);
}
} else {
println!("weird extern crate without a crate id");
}
},
hir::ItemKind::Use(path, ref kind) => println!("{:?}, {:?}", path, kind),
hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)),
hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)),
hir::ItemKind::Fn(..) => {
let item_ty = cx.tcx.type_of(did);
println!("function of type {:#?}", item_ty);
},
hir::ItemKind::Macro(ref macro_def, _) => {
if macro_def.macro_rules {
println!("macro introduced by `macro_rules!`");
} else {
println!("macro introduced by `macro`");
}
},
hir::ItemKind::Mod(..) => println!("module"),
hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi),
hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm),
hir::ItemKind::TyAlias(..) => {
println!("type alias for {:?}", cx.tcx.type_of(did));
},
hir::ItemKind::OpaqueTy(..) => {
println!("existential type with real type {:?}", cx.tcx.type_of(did));
},
hir::ItemKind::Enum(..) => {
println!("enum definition of type {:?}", cx.tcx.type_of(did));
},
hir::ItemKind::Struct(..) => {
println!("struct definition of type {:?}", cx.tcx.type_of(did));
},
hir::ItemKind::Union(..) => {
println!("union definition of type {:?}", cx.tcx.type_of(did));
},
hir::ItemKind::Trait(..) => {
println!("trait decl");
if cx.tcx.trait_is_auto(did.to_def_id()) {
println!("trait is auto");
} else {
println!("trait is not auto");
}
},
hir::ItemKind::TraitAlias(..) => {
println!("trait alias");
},
hir::ItemKind::Impl(hir::Impl {
of_trait: Some(ref _trait_ref),
..
}) => {
println!("trait impl");
},
hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) => {
println!("impl");
},
}
}
#[allow(clippy::similar_names)]
#[allow(clippy::too_many_lines)]
fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) {
let ind = " ".repeat(indent);
println!("{}+", ind);
match pat.kind {
hir::PatKind::Wild => println!("{}Wild", ind),
hir::PatKind::Binding(ref mode, .., ident, ref inner) => {
println!("{}Binding", ind);
println!("{}mode: {:?}", ind, mode);
println!("{}name: {}", ind, ident.name);
if let Some(inner) = *inner {
println!("{}inner:", ind);
print_pat(cx, inner, indent + 1);
}
},
hir::PatKind::Or(fields) => {
println!("{}Or", ind);
for field in fields {
print_pat(cx, field, indent + 1);
}
},
hir::PatKind::Struct(ref path, fields, ignore) => {
println!("{}Struct", ind);
println!(
"{}name: {}",
ind,
rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
);
println!("{}ignore leftover fields: {}", ind, ignore);
println!("{}fields:", ind);
for field in fields {
println!("{} field name: {}", ind, field.ident.name);
if field.is_shorthand {
println!("{} in shorthand notation", ind);
}
print_pat(cx, field.pat, indent + 1);
}
},
hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => {
println!("{}TupleStruct", ind);
println!(
"{}path: {}",
ind,
rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
);
if let Some(dot_position) = opt_dots_position {
println!("{}dot position: {}", ind, dot_position);
}
for field in fields {
print_pat(cx, field, indent + 1);
}
},
hir::PatKind::Path(hir::QPath::Resolved(ref ty, path)) => {
println!("{}Resolved Path, {:?}", ind, ty);
println!("{}path: {:?}", ind, path);
},
hir::PatKind::Path(hir::QPath::TypeRelative(ty, seg)) => {
println!("{}Relative Path, {:?}", ind, ty);
println!("{}seg: {:?}", ind, seg);
},
hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => {
println!("{}Lang Item Path, {:?}", ind, lang_item.name());
},
hir::PatKind::Tuple(pats, opt_dots_position) => {
println!("{}Tuple", ind);
if let Some(dot_position) = opt_dots_position {
println!("{}dot position: {}", ind, dot_position);
}
for field in pats {
print_pat(cx, field, indent + 1);
}
},
hir::PatKind::Box(inner) => {
println!("{}Box", ind);
print_pat(cx, inner, indent + 1);
},
hir::PatKind::Ref(inner, ref muta) => {
println!("{}Ref", ind);
println!("{}mutability: {:?}", ind, muta);
print_pat(cx, inner, indent + 1);
},
hir::PatKind::Lit(e) => {
println!("{}Lit", ind);
print_expr(cx, e, indent + 1);
},
hir::PatKind::Range(ref l, ref r, ref range_end) => {
println!("{}Range", ind);
if let Some(expr) = l {
print_expr(cx, expr, indent + 1);
}
if let Some(expr) = r {
print_expr(cx, expr, indent + 1);
}
match *range_end {
hir::RangeEnd::Included => println!("{} end included", ind),
hir::RangeEnd::Excluded => println!("{} end excluded", ind),
}
},
hir::PatKind::Slice(first_pats, ref range, last_pats) => {
println!("{}Slice [a, b, ..i, y, z]", ind);
println!("[a, b]:");
for pat in first_pats {
print_pat(cx, pat, indent + 1);
}
println!("i:");
if let Some(pat) = *range {
print_pat(cx, pat, indent + 1);
}
println!("[y, z]:");
for pat in last_pats {
print_pat(cx, pat, indent + 1);
}
},
}
}
fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) {
let ind = " ".repeat(indent);
println!("{}+", ind);
match guard {
hir::Guard::If(expr) => {
println!("{}If", ind);
print_expr(cx, expr, indent + 1);
},
hir::Guard::IfLet(pat, expr) => {
println!("{}IfLet", ind);
print_pat(cx, pat, indent + 1);
print_expr(cx, expr, indent + 1);
},
}
}

View File

@ -33,7 +33,7 @@
/// This is the output file of the lint collector. /// This is the output file of the lint collector.
const OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
/// These lints are excluded from the export. /// These lints are excluded from the export.
const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"]; const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"];
/// These groups will be ignored by the lint group matcher. This is useful for collections like /// These groups will be ignored by the lint group matcher. This is useful for collections like
/// `clippy::all` /// `clippy::all`
const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"]; const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];

View File

@ -1,5 +1,5 @@
pub mod author; pub mod author;
pub mod conf; pub mod conf;
pub mod inspector; pub mod dump_hir;
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
pub mod internal_lints; pub mod internal_lints;

View File

@ -22,6 +22,7 @@ because that's clearly a non-descriptive name.
- [Adding the lint logic](#adding-the-lint-logic) - [Adding the lint logic](#adding-the-lint-logic)
- [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv) - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv)
- [Author lint](#author-lint) - [Author lint](#author-lint)
- [Print HIR lint](#print-hir-lint)
- [Documentation](#documentation) - [Documentation](#documentation)
- [Running rustfmt](#running-rustfmt) - [Running rustfmt](#running-rustfmt)
- [Debugging](#debugging) - [Debugging](#debugging)
@ -484,6 +485,19 @@ you are implementing your lint.
[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 [author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
## Print HIR lint
To implement a lint, it's helpful to first understand the internal representation
that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the
[_High-Level Intermediate Representation (HIR)_] of the item, statement, or
expression that the attribute is attached to. To attach the attribute to expressions
you often need to enable `#![feature(stmt_expr_attributes)]`.
[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_.
[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html
[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb
## Documentation ## Documentation
The final thing before submitting our PR is to add some documentation to our The final thing before submitting our PR is to add some documentation to our