about summary refs log tree commit diff
path: root/clippy_lints/src/utils
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2024-11-07 22:31:20 +0100
committerPhilipp Krones <hello@philkrones.com>2024-11-07 22:37:01 +0100
commit6ced8c33c058fa1df65a363abcdc5e2c5828fa66 (patch)
tree86a24d4cb4011e4f95093ed710c7bb35cdd95304 /clippy_lints/src/utils
parent4847c40c8b40cc2a1155204b934f7a3c29178782 (diff)
downloadrust-6ced8c33c058fa1df65a363abcdc5e2c5828fa66.tar.gz
rust-6ced8c33c058fa1df65a363abcdc5e2c5828fa66.zip
Merge commit 'f712eb5cdccd121d0569af12f20e6a0fabe4364d' into clippy-subtree-update
Diffstat (limited to 'clippy_lints/src/utils')
-rw-r--r--clippy_lints/src/utils/attr_collector.rs40
-rw-r--r--clippy_lints/src/utils/author.rs2
-rw-r--r--clippy_lints/src/utils/internal_lints.rs1
-rw-r--r--clippy_lints/src/utils/internal_lints/collapsible_calls.rs2
-rw-r--r--clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs14
-rw-r--r--clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs2
-rw-r--r--clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs69
-rw-r--r--clippy_lints/src/utils/mod.rs2
8 files changed, 121 insertions, 11 deletions
diff --git a/clippy_lints/src/utils/attr_collector.rs b/clippy_lints/src/utils/attr_collector.rs
new file mode 100644
index 00000000000..1522553bbf5
--- /dev/null
+++ b/clippy_lints/src/utils/attr_collector.rs
@@ -0,0 +1,40 @@
+use std::mem;
+use std::sync::OnceLock;
+
+use rustc_ast::{Attribute, Crate};
+use rustc_data_structures::sync::Lrc;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::impl_lint_pass;
+use rustc_span::Span;
+
+#[derive(Clone, Default)]
+pub struct AttrStorage(pub Lrc<OnceLock<Vec<Span>>>);
+
+pub struct AttrCollector {
+    storage: AttrStorage,
+    attrs: Vec<Span>,
+}
+
+impl AttrCollector {
+    pub fn new(storage: AttrStorage) -> Self {
+        Self {
+            storage,
+            attrs: Vec::new(),
+        }
+    }
+}
+
+impl_lint_pass!(AttrCollector => []);
+
+impl EarlyLintPass for AttrCollector {
+    fn check_attribute(&mut self, _cx: &EarlyContext<'_>, attr: &Attribute) {
+        self.attrs.push(attr.span);
+    }
+
+    fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) {
+        self.storage
+            .0
+            .set(mem::take(&mut self.attrs))
+            .expect("should only be called once");
+    }
+}
diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs
index 31f9d84f5e4..1bf24083665 100644
--- a/clippy_lints/src/utils/author.rs
+++ b/clippy_lints/src/utils/author.rs
@@ -396,7 +396,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                 self.pat(field!(let_expr.pat));
                 // Does what ExprKind::Cast does, only adds a clause for the type
                 // if it's a path
-                if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) {
+                if let Some(TyKind::Path(qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) {
                     bind!(self, qpath);
                     chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind");
                     self.qpath(qpath);
diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs
index f662c7651f6..deb983b6971 100644
--- a/clippy_lints/src/utils/internal_lints.rs
+++ b/clippy_lints/src/utils/internal_lints.rs
@@ -6,5 +6,6 @@ pub mod lint_without_lint_pass;
 pub mod msrv_attr_impl;
 pub mod outer_expn_data_pass;
 pub mod produce_ice;
+pub mod slow_symbol_comparisons;
 pub mod unnecessary_def_path;
 pub mod unsorted_clippy_utils_paths;
diff --git a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
index a3f9abe4f96..eaeb754a23f 100644
--- a/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
+++ b/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
@@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
 
         if let ExprKind::Call(func, [call_cx, call_lint, call_sp, call_msg, call_f]) = expr.kind
             && is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"])
-            && let ExprKind::Closure(&Closure { body, .. }) = &call_f.kind
+            && let ExprKind::Closure(&Closure { body, .. }) = call_f.kind
             && let body = cx.tcx.hir().body(body)
             && let only_expr = peel_blocks_with_stmt(body.value)
             && let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind
diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
index 51235de9f29..af38e066559 100644
--- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
+++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
     ///
     /// ### Why is this bad?
     /// The compiler only knows lints via a `LintPass`. Without
-    /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
+    /// putting a lint to a `LintPass::lint_vec()`'s return, the compiler will not
     /// know the name of the lint.
     ///
     /// ### Known problems
@@ -123,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
                     .expect("lints must have a description field");
 
                 if let ExprKind::Lit(Spanned {
-                    node: LitKind::Str(ref sym, _),
+                    node: LitKind::Str(sym, _),
                     ..
                 }) = field.expr.kind
                 {
@@ -159,8 +159,8 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
                 let body = cx.tcx.hir().body_owned_by(
                     impl_item_refs
                         .iter()
-                        .find(|iiref| iiref.ident.as_str() == "get_lints")
-                        .expect("LintPass needs to implement get_lints")
+                        .find(|iiref| iiref.ident.as_str() == "lint_vec")
+                        .expect("LintPass needs to implement lint_vec")
                         .id
                         .owner_id
                         .def_id,
@@ -218,9 +218,7 @@ pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 
 fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
     if let Some(value) = extract_clippy_version_value(cx, item) {
-        // The `sym!` macro doesn't work as it only expects a single token.
-        // It's better to keep it this way and have a direct `Symbol::intern` call here.
-        if value == Symbol::intern("pre 1.29.0") {
+        if value.as_str() == "pre 1.29.0" {
             return;
         }
 
@@ -251,7 +249,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'
 pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
     let attrs = cx.tcx.hir().attrs(item.hir_id());
     attrs.iter().find_map(|attr| {
-        if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind
+        if let ast::AttrKind::Normal(attr_kind) = &attr.kind
             // Identify attribute
             && let [tool_name, attr_name] = &attr_kind.item.path.segments[..]
             && tool_name.ident.name == sym::clippy
diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
index 63fcbd61528..68692246153 100644
--- a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
+++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
@@ -42,7 +42,7 @@ impl LateLintPass<'_> for MsrvAttrImpl {
                     .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
                     .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV))
             })
-            && !items.iter().any(|item| item.ident.name == sym!(check_attributes))
+            && !items.iter().any(|item| item.ident.name.as_str() == "check_attributes")
         {
             let context = if is_late_pass { "LateContext" } else { "EarlyContext" };
             let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" };
diff --git a/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs b/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs
new file mode 100644
index 00000000000..3742be0e103
--- /dev/null
+++ b/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs
@@ -0,0 +1,69 @@
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::match_type;
+use clippy_utils::{match_function_call, paths};
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Detects symbol comparision using `Symbol::intern`.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Comparision via `Symbol::as_str()` is faster if the interned symbols are not reused.
+    ///
+    /// ### Example
+    ///
+    /// None, see suggestion.
+    pub SLOW_SYMBOL_COMPARISONS,
+    internal,
+    "detects slow comparisions of symbol"
+}
+
+declare_lint_pass!(SlowSymbolComparisons => [SLOW_SYMBOL_COMPARISONS]);
+
+fn check_slow_comparison<'tcx>(
+    cx: &LateContext<'tcx>,
+    op1: &'tcx Expr<'tcx>,
+    op2: &'tcx Expr<'tcx>,
+) -> Option<(Span, String)> {
+    if match_type(cx, cx.typeck_results().expr_ty(op1), &paths::SYMBOL)
+        && let Some([symbol_name_expr]) = match_function_call(cx, op2, &paths::SYMBOL_INTERN)
+        && let Some(Constant::Str(symbol_name)) = ConstEvalCtxt::new(cx).eval_simple(symbol_name_expr)
+    {
+        Some((op1.span, symbol_name))
+    } else {
+        None
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for SlowSymbolComparisons {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+        if let ExprKind::Binary(op, left, right) = expr.kind
+            && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne)
+            && let Some((symbol_span, symbol_name)) =
+                check_slow_comparison(cx, left, right).or_else(|| check_slow_comparison(cx, right, left))
+        {
+            let mut applicability = Applicability::MachineApplicable;
+            span_lint_and_sugg(
+                cx,
+                SLOW_SYMBOL_COMPARISONS,
+                expr.span,
+                "comparing `Symbol` via `Symbol::intern`",
+                "use `Symbol::as_str` and check the string instead",
+                format!(
+                    "{}.as_str() {} \"{symbol_name}\"",
+                    snippet_with_applicability(cx, symbol_span, "symbol", &mut applicability),
+                    op.node.as_str()
+                ),
+                applicability,
+            );
+        };
+    }
+}
diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs
index 13e9ead9a57..4476cd1005e 100644
--- a/clippy_lints/src/utils/mod.rs
+++ b/clippy_lints/src/utils/mod.rs
@@ -1,5 +1,7 @@
+pub mod attr_collector;
 pub mod author;
 pub mod dump_hir;
 pub mod format_args_collector;
+
 #[cfg(feature = "internal")]
 pub mod internal_lints;