use clippy_utils::diagnostics::span_lint; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; declare_clippy_lint! { /// ### What it does /// Checks for comparisons with an address of a function item. /// /// ### Why is this bad? /// Function item address is not guaranteed to be unique and could vary /// between different code generation units. Furthermore different function items could have /// the same address after being merged together. /// /// ### Example /// ```no_run /// type F = fn(); /// fn a() {} /// let f: F = a; /// if f == a { /// // ... /// } /// ``` #[clippy::version = "1.44.0"] pub FN_ADDRESS_COMPARISONS, correctness, "comparison with an address of a function item" } declare_lint_pass!(UnnamedAddress => [FN_ADDRESS_COMPARISONS]); impl LateLintPass<'_> for UnnamedAddress { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn is_comparison(binop: BinOpKind) -> bool { matches!( binop, BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt ) } fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!(cx.typeck_results().expr_ty(expr).kind(), ty::FnDef(..)) } if let ExprKind::Binary(binop, left, right) = expr.kind && is_comparison(binop.node) && cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr() && cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr() && (is_fn_def(cx, left) || is_fn_def(cx, right)) { span_lint( cx, FN_ADDRESS_COMPARISONS, expr.span, "comparing with a non-unique address of a function item", ); } } }