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