]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/unnamed_address.rs
Rollup merge of #87522 - frogtd:patch-1, r=yaahc
[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     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
34     /// Checks for comparisons with an address of a trait vtable.
35     ///
36     /// ### Why is this bad?
37     /// Comparing trait objects pointers compares an vtable addresses which
38     /// are not guaranteed to be unique and could vary between different code generation units.
39     /// Furthermore vtables for different types could have the same address after being merged
40     /// together.
41     ///
42     /// ### Example
43     /// ```rust,ignore
44     /// let a: Rc<dyn Trait> = ...
45     /// let b: Rc<dyn Trait> = ...
46     /// if Rc::ptr_eq(&a, &b) {
47     ///     ...
48     /// }
49     /// ```
50     pub VTABLE_ADDRESS_COMPARISONS,
51     correctness,
52     "comparison with an address of a trait vtable"
53 }
54
55 declare_lint_pass!(UnnamedAddress => [FN_ADDRESS_COMPARISONS, VTABLE_ADDRESS_COMPARISONS]);
56
57 impl LateLintPass<'_> for UnnamedAddress {
58     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
59         fn is_comparison(binop: BinOpKind) -> bool {
60             matches!(
61                 binop,
62                 BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt
63             )
64         }
65
66         fn is_trait_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
67             match cx.typeck_results().expr_ty_adjusted(expr).kind() {
68                 ty::RawPtr(ty::TypeAndMut { ty, .. }) => ty.is_trait(),
69                 _ => false,
70             }
71         }
72
73         fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
74             matches!(cx.typeck_results().expr_ty(expr).kind(), ty::FnDef(..))
75         }
76
77         if_chain! {
78             if let ExprKind::Binary(binop, left, right) = expr.kind;
79             if is_comparison(binop.node);
80             if is_trait_ptr(cx, left) && is_trait_ptr(cx, right);
81             then {
82                 span_lint_and_help(
83                     cx,
84                     VTABLE_ADDRESS_COMPARISONS,
85                     expr.span,
86                     "comparing trait object pointers compares a non-unique vtable address",
87                     None,
88                     "consider extracting and comparing data pointers only",
89                 );
90             }
91         }
92
93         if_chain! {
94             if let ExprKind::Call(func, [ref _left, ref _right]) = expr.kind;
95             if let ExprKind::Path(ref func_qpath) = func.kind;
96             if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
97             if match_def_path(cx, def_id, &paths::PTR_EQ) ||
98                 match_def_path(cx, def_id, &paths::RC_PTR_EQ) ||
99                 match_def_path(cx, def_id, &paths::ARC_PTR_EQ);
100             let ty_param = cx.typeck_results().node_substs(func.hir_id).type_at(0);
101             if ty_param.is_trait();
102             then {
103                 span_lint_and_help(
104                     cx,
105                     VTABLE_ADDRESS_COMPARISONS,
106                     expr.span,
107                     "comparing trait object pointers compares a non-unique vtable address",
108                     None,
109                     "consider extracting and comparing data pointers only",
110                 );
111             }
112         }
113
114         if_chain! {
115             if let ExprKind::Binary(binop, left, right) = expr.kind;
116             if is_comparison(binop.node);
117             if cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr();
118             if cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr();
119             if is_fn_def(cx, left) || is_fn_def(cx, right);
120             then {
121                 span_lint(
122                     cx,
123                     FN_ADDRESS_COMPARISONS,
124                     expr.span,
125                     "comparing with a non-unique address of a function item",
126                 );
127             }
128         }
129     }
130 }