]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 XL |
1 | use clippy_utils::diagnostics::span_lint_and_then; |
2 | use clippy_utils::source::snippet; | |
3 | use clippy_utils::ty::walk_ptrs_ty_depth; | |
4 | use clippy_utils::{match_def_path, paths}; | |
f20569fa XL |
5 | use if_chain::if_chain; |
6 | use rustc_errors::Applicability; | |
7 | use rustc_hir::{BorrowKind, Expr, ExprKind}; | |
8 | use rustc_lint::{LateContext, LateLintPass}; | |
9 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
f20569fa XL |
10 | |
11 | declare_clippy_lint! { | |
94222f64 XL |
12 | /// ### What it does |
13 | /// Checks for calls of `mem::discriminant()` on a non-enum type. | |
f20569fa | 14 | /// |
94222f64 XL |
15 | /// ### Why is this bad? |
16 | /// The value of `mem::discriminant()` on non-enum types | |
f20569fa XL |
17 | /// is unspecified. |
18 | /// | |
94222f64 | 19 | /// ### Example |
f20569fa XL |
20 | /// ```rust |
21 | /// use std::mem; | |
22 | /// | |
23 | /// mem::discriminant(&"hello"); | |
24 | /// mem::discriminant(&&Some(2)); | |
25 | /// ``` | |
26 | pub MEM_DISCRIMINANT_NON_ENUM, | |
27 | correctness, | |
28 | "calling `mem::descriminant` on non-enum type" | |
29 | } | |
30 | ||
31 | declare_lint_pass!(MemDiscriminant => [MEM_DISCRIMINANT_NON_ENUM]); | |
32 | ||
33 | impl<'tcx> LateLintPass<'tcx> for MemDiscriminant { | |
34 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | |
35 | if_chain! { | |
cdc7bbd5 | 36 | if let ExprKind::Call(func, func_args) = expr.kind; |
f20569fa XL |
37 | // is `mem::discriminant` |
38 | if let ExprKind::Path(ref func_qpath) = func.kind; | |
39 | if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); | |
40 | if match_def_path(cx, def_id, &paths::MEM_DISCRIMINANT); | |
41 | // type is non-enum | |
42 | let ty_param = cx.typeck_results().node_substs(func.hir_id).type_at(0); | |
43 | if !ty_param.is_enum(); | |
44 | ||
45 | then { | |
46 | span_lint_and_then( | |
47 | cx, | |
48 | MEM_DISCRIMINANT_NON_ENUM, | |
49 | expr.span, | |
50 | &format!("calling `mem::discriminant` on non-enum type `{}`", ty_param), | |
51 | |diag| { | |
52 | // if this is a reference to an enum, suggest dereferencing | |
53 | let (base_ty, ptr_depth) = walk_ptrs_ty_depth(ty_param); | |
54 | if ptr_depth >= 1 && base_ty.is_enum() { | |
55 | let param = &func_args[0]; | |
56 | ||
57 | // cancel out '&'s first | |
58 | let mut derefs_needed = ptr_depth; | |
59 | let mut cur_expr = param; | |
60 | while derefs_needed > 0 { | |
cdc7bbd5 | 61 | if let ExprKind::AddrOf(BorrowKind::Ref, _, inner_expr) = cur_expr.kind { |
f20569fa XL |
62 | derefs_needed -= 1; |
63 | cur_expr = inner_expr; | |
64 | } else { | |
65 | break; | |
66 | } | |
67 | } | |
68 | ||
17df50a5 | 69 | let derefs = "*".repeat(derefs_needed); |
f20569fa XL |
70 | diag.span_suggestion( |
71 | param.span, | |
72 | "try dereferencing", | |
73 | format!("{}{}", derefs, snippet(cx, cur_expr.span, "<param>")), | |
74 | Applicability::MachineApplicable, | |
75 | ); | |
76 | } | |
77 | }, | |
78 | ) | |
79 | } | |
80 | } | |
81 | } | |
82 | } |