about summary refs log tree commit diff
path: root/clippy_lints_internal
diff options
context:
space:
mode:
authorAlex Macleod <alex@macleod.io>2025-04-23 13:24:01 +0000
committerAlex Macleod <alex@macleod.io>2025-04-25 01:03:03 +0000
commit7b337f6e259ce3ae19e1c0c22c8ff493ecb4a8a3 (patch)
tree2eb9e277472f520863f1cf25a5f6dbd682555c80 /clippy_lints_internal
parentaa27ae3fee9f112699ad5e4d6efb2245388a77f1 (diff)
downloadrust-7b337f6e259ce3ae19e1c0c22c8ff493ecb4a8a3.tar.gz
rust-7b337f6e259ce3ae19e1c0c22c8ff493ecb4a8a3.zip
Replace some `Symbol::as_str` usage
Diffstat (limited to 'clippy_lints_internal')
-rw-r--r--clippy_lints_internal/src/lib.rs8
-rw-r--r--clippy_lints_internal/src/symbols.rs (renamed from clippy_lints_internal/src/interning_literals.rs)99
2 files changed, 88 insertions, 19 deletions
diff --git a/clippy_lints_internal/src/lib.rs b/clippy_lints_internal/src/lib.rs
index 1c42f4112f9..b02d378619c 100644
--- a/clippy_lints_internal/src/lib.rs
+++ b/clippy_lints_internal/src/lib.rs
@@ -2,6 +2,7 @@
 #![allow(
     clippy::missing_docs_in_private_items,
     clippy::must_use_candidate,
+    clippy::symbol_as_str,
     rustc::diagnostic_outside_of_impl,
     rustc::untranslatable_diagnostic
 )]
@@ -31,12 +32,12 @@ extern crate rustc_span;
 
 mod almost_standard_lint_formulation;
 mod collapsible_calls;
-mod interning_literals;
 mod invalid_paths;
 mod lint_without_lint_pass;
 mod msrv_attr_impl;
 mod outer_expn_data_pass;
 mod produce_ice;
+mod symbols;
 mod unnecessary_def_path;
 mod unsorted_clippy_utils_paths;
 
@@ -45,7 +46,6 @@ use rustc_lint::{Lint, LintStore};
 static LINTS: &[&Lint] = &[
     almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION,
     collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS,
-    interning_literals::INTERNING_LITERALS,
     invalid_paths::INVALID_PATHS,
     lint_without_lint_pass::DEFAULT_LINT,
     lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE,
@@ -54,6 +54,8 @@ static LINTS: &[&Lint] = &[
     msrv_attr_impl::MISSING_MSRV_ATTR_IMPL,
     outer_expn_data_pass::OUTER_EXPN_EXPN_DATA,
     produce_ice::PRODUCE_ICE,
+    symbols::INTERNING_LITERALS,
+    symbols::SYMBOL_AS_STR,
     unnecessary_def_path::UNNECESSARY_DEF_PATH,
     unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS,
 ];
@@ -65,7 +67,7 @@ pub fn register_lints(store: &mut LintStore) {
     store.register_early_pass(|| Box::new(produce_ice::ProduceIce));
     store.register_late_pass(|_| Box::new(collapsible_calls::CollapsibleCalls));
     store.register_late_pass(|_| Box::new(invalid_paths::InvalidPaths));
-    store.register_late_pass(|_| Box::<interning_literals::InterningDefinedSymbol>::default());
+    store.register_late_pass(|_| Box::<symbols::Symbols>::default());
     store.register_late_pass(|_| Box::<lint_without_lint_pass::LintWithoutLintPass>::default());
     store.register_late_pass(|_| Box::<unnecessary_def_path::UnnecessaryDefPath>::default());
     store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass));
diff --git a/clippy_lints_internal/src/interning_literals.rs b/clippy_lints_internal/src/symbols.rs
index 6cee3744234..c64e5821916 100644
--- a/clippy_lints_internal/src/interning_literals.rs
+++ b/clippy_lints_internal/src/symbols.rs
@@ -1,7 +1,7 @@
-use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::match_type;
-use clippy_utils::{def_path_def_ids, paths};
+use clippy_utils::{def_path_def_ids, match_def_path, paths};
+use rustc_ast::LitKind;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
@@ -11,8 +11,8 @@ use rustc_lint_defs::declare_tool_lint;
 use rustc_middle::mir::ConstValue;
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
-use rustc_span::sym;
 use rustc_span::symbol::Symbol;
+use rustc_span::{Span, sym};
 
 declare_tool_lint! {
     /// ### What it does
@@ -36,15 +36,37 @@ declare_tool_lint! {
     report_in_external_macro: true
 }
 
+declare_tool_lint! {
+    /// ### What it does
+    /// Checks for calls to `Symbol::as_str`
+    ///
+    /// ### Why is this bad?
+    /// It's faster and easier to use the symbol constant. If one doesn't exist it can be added to `clippy_utils/src/sym.rs`
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// symbol.as_str() == "foo"
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust,ignore
+    /// symbol == sym::foo
+    /// ```
+    pub clippy::SYMBOL_AS_STR,
+    Warn,
+    "calls to `Symbol::as_str`",
+    report_in_external_macro: true
+}
+
 #[derive(Default)]
-pub struct InterningDefinedSymbol {
+pub struct Symbols {
     // Maps the symbol to the import path
     symbol_map: FxHashMap<u32, (&'static str, Symbol)>,
 }
 
-impl_lint_pass!(InterningDefinedSymbol => [INTERNING_LITERALS]);
+impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]);
 
-impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
+impl<'tcx> LateLintPass<'tcx> for Symbols {
     fn check_crate(&mut self, cx: &LateContext<'_>) {
         let modules = [
             ("kw", &paths::KW_MODULE[..]),
@@ -77,7 +99,8 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
         if let ExprKind::Call(func, [arg]) = &expr.kind
             && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind()
             && cx.tcx.is_diagnostic_item(sym::SymbolIntern, *def_id)
-            && let Some(Constant::Str(arg)) = ConstEvalCtxt::new(cx).eval_simple(arg)
+            && let ExprKind::Lit(lit) = arg.kind
+            && let LitKind::Str(name, _) = lit.node
         {
             span_lint_and_then(
                 cx,
@@ -85,18 +108,62 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
                 expr.span,
                 "interning a string literal",
                 |diag| {
-                    let value = Symbol::intern(&arg).as_u32();
-                    let (message, path) = if let Some((prefix, name)) = self.symbol_map.get(&value) {
-                        ("use the preinterned symbol", format!("{prefix}::{name}"))
-                    } else {
-                        (
-                            "add the symbol to `clippy_utils/src/sym.rs` and use it",
-                            format!("sym::{}", arg.replace(|ch: char| !ch.is_alphanumeric(), "_")),
-                        )
-                    };
+                    let (message, path) = suggestion(&mut self.symbol_map, name);
                     diag.span_suggestion_verbose(expr.span, message, path, Applicability::MaybeIncorrect);
                 },
             );
         }
+
+        if let ExprKind::Binary(_, lhs, rhs) = expr.kind {
+            check_binary(cx, lhs, rhs, &mut self.symbol_map);
+            check_binary(cx, rhs, lhs, &mut self.symbol_map);
+        }
+    }
+}
+
+fn check_binary(
+    cx: &LateContext<'_>,
+    lhs: &Expr<'_>,
+    rhs: &Expr<'_>,
+    symbols: &mut FxHashMap<u32, (&'static str, Symbol)>,
+) {
+    if let Some(removal_span) = as_str_span(cx, lhs)
+        && let ExprKind::Lit(lit) = rhs.kind
+        && let LitKind::Str(name, _) = lit.node
+    {
+        span_lint_and_then(cx, SYMBOL_AS_STR, lhs.span, "converting a Symbol to a string", |diag| {
+            let (message, path) = suggestion(symbols, name);
+            diag.multipart_suggestion_verbose(
+                message,
+                vec![(removal_span, String::new()), (rhs.span, path)],
+                Applicability::MachineApplicable,
+            );
+        });
+    }
+}
+
+fn suggestion(symbols: &mut FxHashMap<u32, (&'static str, Symbol)>, name: Symbol) -> (&'static str, String) {
+    if let Some((prefix, name)) = symbols.get(&name.as_u32()) {
+        ("use the preinterned symbol", format!("{prefix}::{name}"))
+    } else {
+        (
+            "add the symbol to `clippy_utils/src/sym.rs` and use it",
+            format!("sym::{}", name.as_str().replace(|ch: char| !ch.is_alphanumeric(), "_")),
+        )
+    }
+}
+
+/// ```ignore
+/// symbol.as_str()
+/// //     ^^^^^^^^
+/// ```
+fn as_str_span(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> {
+    if let ExprKind::MethodCall(_, recv, [], _) = expr.kind
+        && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
+        && match_def_path(cx, method_def_id, &paths::SYMBOL_AS_STR)
+    {
+        Some(recv.span.shrink_to_hi().to(expr.span.shrink_to_hi()))
+    } else {
+        None
     }
 }