Skip to content

Commit 6b1779b

Browse files
committed
Add lint to check for slow symbol comparisons
1 parent 236751d commit 6b1779b

File tree

4 files changed

+73
-0
lines changed

4 files changed

+73
-0
lines changed

clippy_lints/src/declared_lints.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub static LINTS: &[&crate::LintInfo] = &[
2828
#[cfg(feature = "internal")]
2929
crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO,
3030
#[cfg(feature = "internal")]
31+
crate::utils::internal_lints::slow_symbol_comparisons::SLOW_SYMBOL_COMPARISONS_INFO,
32+
#[cfg(feature = "internal")]
3133
crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO,
3234
#[cfg(feature = "internal")]
3335
crate::utils::internal_lints::unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS_INFO,

clippy_lints/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
605605
store.register_late_pass(|_| {
606606
Box::new(utils::internal_lints::almost_standard_lint_formulation::AlmostStandardFormulation::new())
607607
});
608+
store.register_late_pass(|_| Box::new(utils::internal_lints::slow_symbol_comparisons::SlowSymbolComparisons));
608609
}
609610

610611
store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf)));

clippy_lints/src/utils/internal_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ pub mod lint_without_lint_pass;
66
pub mod msrv_attr_impl;
77
pub mod outer_expn_data_pass;
88
pub mod produce_ice;
9+
pub mod slow_symbol_comparisons;
910
pub mod unnecessary_def_path;
1011
pub mod unsorted_clippy_utils_paths;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use clippy_utils::consts::{ConstEvalCtxt, Constant};
2+
use clippy_utils::diagnostics::span_lint_and_sugg;
3+
use clippy_utils::source::snippet_with_applicability;
4+
use clippy_utils::ty::match_type;
5+
use clippy_utils::{match_function_call, paths};
6+
use rustc_errors::Applicability;
7+
use rustc_hir::{BinOpKind, Expr, ExprKind};
8+
use rustc_lint::{LateContext, LateLintPass};
9+
use rustc_session::declare_lint_pass;
10+
use rustc_span::Span;
11+
12+
declare_clippy_lint! {
13+
/// ### What it does
14+
///
15+
/// Detects symbol comparision using `Symbol::intern`.
16+
///
17+
/// ### Why is this bad?
18+
///
19+
/// Comparision via `Symbol::as_str()` is faster if the interned symbols are not reused.
20+
///
21+
/// ### Example
22+
///
23+
/// None, see suggestion.
24+
pub SLOW_SYMBOL_COMPARISONS,
25+
internal,
26+
"detects slow comparisions of symbol"
27+
}
28+
29+
declare_lint_pass!(SlowSymbolComparisons => [SLOW_SYMBOL_COMPARISONS]);
30+
31+
fn check_slow_comparison<'tcx>(
32+
cx: &LateContext<'tcx>,
33+
op1: &'tcx Expr<'tcx>,
34+
op2: &'tcx Expr<'tcx>,
35+
) -> Option<(Span, String)> {
36+
if match_type(cx, cx.typeck_results().expr_ty(op1), &paths::SYMBOL)
37+
&& let Some([symbol_name_expr]) = match_function_call(cx, op2, &paths::SYMBOL_INTERN)
38+
&& let Some(Constant::Str(symbol_name)) = ConstEvalCtxt::new(cx).eval_simple(symbol_name_expr)
39+
{
40+
Some((op1.span, symbol_name))
41+
} else {
42+
None
43+
}
44+
}
45+
46+
impl<'tcx> LateLintPass<'tcx> for SlowSymbolComparisons {
47+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
48+
if let ExprKind::Binary(op, left, right) = expr.kind
49+
&& (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne)
50+
&& let Some((symbol_span, symbol_name)) =
51+
check_slow_comparison(cx, left, right).or_else(|| check_slow_comparison(cx, right, left))
52+
{
53+
let mut applicability = Applicability::MachineApplicable;
54+
span_lint_and_sugg(
55+
cx,
56+
SLOW_SYMBOL_COMPARISONS,
57+
expr.span,
58+
"comparing `Symbol` via `Symbol::intern`",
59+
"use `Symbol::as_str` and check the string instead",
60+
format!(
61+
"{}.as_str() {} \"{symbol_name}\"",
62+
snippet_with_applicability(cx, symbol_span, "symbol", &mut applicability),
63+
op.node.as_str()
64+
),
65+
applicability,
66+
);
67+
};
68+
}
69+
}

0 commit comments

Comments
 (0)