]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/unnamed_address.rs
Rollup merge of #73949 - wesleywiser:simplify_try_fixes, r=oli-obk
[rust.git] / src / tools / clippy / clippy_lints / src / unnamed_address.rs
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             match binop {
62                 BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt => true,
63                 _ => false,
64             }
65         }
66
67         fn is_trait_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
68             match cx.tables().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             if let ty::FnDef(..) = cx.tables().expr_ty(expr).kind {
76                 true
77             } else {
78                 false
79             }
80         }
81
82         if_chain! {
83             if let ExprKind::Binary(binop, ref left, ref right) = expr.kind;
84             if is_comparison(binop.node);
85             if is_trait_ptr(cx, left) && is_trait_ptr(cx, right);
86             then {
87                 span_lint_and_help(
88                     cx,
89                     VTABLE_ADDRESS_COMPARISONS,
90                     expr.span,
91                     "comparing trait object pointers compares a non-unique vtable address",
92                     None,
93                     "consider extracting and comparing data pointers only",
94                 );
95             }
96         }
97
98         if_chain! {
99             if let ExprKind::Call(ref func, [ref _left, ref _right]) = expr.kind;
100             if let ExprKind::Path(ref func_qpath) = func.kind;
101             if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
102             if match_def_path(cx, def_id, &paths::PTR_EQ) ||
103                 match_def_path(cx, def_id, &paths::RC_PTR_EQ) ||
104                 match_def_path(cx, def_id, &paths::ARC_PTR_EQ);
105             let ty_param = cx.tables().node_substs(func.hir_id).type_at(0);
106             if ty_param.is_trait();
107             then {
108                 span_lint_and_help(
109                     cx,
110                     VTABLE_ADDRESS_COMPARISONS,
111                     expr.span,
112                     "comparing trait object pointers compares a non-unique vtable address",
113                     None,
114                     "consider extracting and comparing data pointers only",
115                 );
116             }
117         }
118
119         if_chain! {
120             if let ExprKind::Binary(binop, ref left, ref right) = expr.kind;
121             if is_comparison(binop.node);
122             if cx.tables().expr_ty_adjusted(left).is_fn_ptr() &&
123                 cx.tables().expr_ty_adjusted(right).is_fn_ptr();
124             if is_fn_def(cx, left) || is_fn_def(cx, right);
125             then {
126                 span_lint(
127                     cx,
128                     FN_ADDRESS_COMPARISONS,
129                     expr.span,
130                     "comparing with a non-unique address of a function item",
131                 );
132             }
133         }
134     }
135 }