]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help}; |
2 | use if_chain::if_chain; | |
3 | use rustc_hir::{BinOpKind, Expr, ExprKind}; | |
4 | use rustc_lint::{LateContext, LateLintPass}; | |
5 | use rustc_middle::ty; | |
6 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
7 | ||
8 | declare_clippy_lint! { | |
9 | /// **What it does:** Checks for comparisons with an address of a function item. | |
10 | /// | |
11 | /// **Why is this bad?** Function item address is not guaranteed to be unique and could vary | |
12 | /// between different code generation units. Furthermore different function items could have | |
13 | /// the same address after being merged together. | |
14 | /// | |
15 | /// **Known problems:** None. | |
16 | /// | |
17 | /// **Example:** | |
18 | /// | |
19 | /// ```rust | |
20 | /// type F = fn(); | |
21 | /// fn a() {} | |
22 | /// let f: F = a; | |
23 | /// if f == a { | |
24 | /// // ... | |
25 | /// } | |
26 | /// ``` | |
27 | pub FN_ADDRESS_COMPARISONS, | |
28 | correctness, | |
29 | "comparison with an address of a function item" | |
30 | } | |
31 | ||
32 | declare_clippy_lint! { | |
33 | /// **What it does:** Checks for comparisons with an address of a trait vtable. | |
34 | /// | |
35 | /// **Why is this bad?** Comparing trait objects pointers compares an vtable addresses which | |
36 | /// are not guaranteed to be unique and could vary between different code generation units. | |
37 | /// Furthermore vtables for different types could have the same address after being merged | |
38 | /// together. | |
39 | /// | |
40 | /// **Known problems:** None. | |
41 | /// | |
42 | /// **Example:** | |
43 | /// | |
44 | /// ```rust,ignore | |
45 | /// let a: Rc<dyn Trait> = ... | |
46 | /// let b: Rc<dyn Trait> = ... | |
47 | /// if Rc::ptr_eq(&a, &b) { | |
48 | /// ... | |
49 | /// } | |
50 | /// ``` | |
51 | pub VTABLE_ADDRESS_COMPARISONS, | |
52 | correctness, | |
53 | "comparison with an address of a trait vtable" | |
54 | } | |
55 | ||
56 | declare_lint_pass!(UnnamedAddress => [FN_ADDRESS_COMPARISONS, VTABLE_ADDRESS_COMPARISONS]); | |
57 | ||
58 | impl LateLintPass<'_> for UnnamedAddress { | |
59 | fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { | |
60 | fn is_comparison(binop: BinOpKind) -> bool { | |
61 | matches!( | |
62 | binop, | |
63 | BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt | |
64 | ) | |
65 | } | |
66 | ||
67 | fn is_trait_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | |
68 | match cx.typeck_results().expr_ty_adjusted(expr).kind() { | |
69 | ty::RawPtr(ty::TypeAndMut { ty, .. }) => ty.is_trait(), | |
70 | _ => false, | |
71 | } | |
72 | } | |
73 | ||
74 | fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { | |
75 | matches!(cx.typeck_results().expr_ty(expr).kind(), ty::FnDef(..)) | |
76 | } | |
77 | ||
78 | if_chain! { | |
79 | if let ExprKind::Binary(binop, ref left, ref right) = expr.kind; | |
80 | if is_comparison(binop.node); | |
81 | if is_trait_ptr(cx, left) && is_trait_ptr(cx, right); | |
82 | then { | |
83 | span_lint_and_help( | |
84 | cx, | |
85 | VTABLE_ADDRESS_COMPARISONS, | |
86 | expr.span, | |
87 | "comparing trait object pointers compares a non-unique vtable address", | |
88 | None, | |
89 | "consider extracting and comparing data pointers only", | |
90 | ); | |
91 | } | |
92 | } | |
93 | ||
94 | if_chain! { | |
95 | if let ExprKind::Call(ref func, [ref _left, ref _right]) = expr.kind; | |
96 | if let ExprKind::Path(ref func_qpath) = func.kind; | |
97 | if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id(); | |
98 | if match_def_path(cx, def_id, &paths::PTR_EQ) || | |
99 | match_def_path(cx, def_id, &paths::RC_PTR_EQ) || | |
100 | match_def_path(cx, def_id, &paths::ARC_PTR_EQ); | |
101 | let ty_param = cx.typeck_results().node_substs(func.hir_id).type_at(0); | |
102 | if ty_param.is_trait(); | |
103 | then { | |
104 | span_lint_and_help( | |
105 | cx, | |
106 | VTABLE_ADDRESS_COMPARISONS, | |
107 | expr.span, | |
108 | "comparing trait object pointers compares a non-unique vtable address", | |
109 | None, | |
110 | "consider extracting and comparing data pointers only", | |
111 | ); | |
112 | } | |
113 | } | |
114 | ||
115 | if_chain! { | |
116 | if let ExprKind::Binary(binop, ref left, ref right) = expr.kind; | |
117 | if is_comparison(binop.node); | |
118 | if cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr() && | |
119 | cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr(); | |
120 | if is_fn_def(cx, left) || is_fn_def(cx, right); | |
121 | then { | |
122 | span_lint( | |
123 | cx, | |
124 | FN_ADDRESS_COMPARISONS, | |
125 | expr.span, | |
126 | "comparing with a non-unique address of a function item", | |
127 | ); | |
128 | } | |
129 | } | |
130 | } | |
131 | } |