Skip to content

Commit 3af09b8

Browse files
author
Michael Wright
committed
New internal lint: interning_defined_symbol
1 parent 89c282f commit 3af09b8

File tree

6 files changed

+183
-0
lines changed

6 files changed

+183
-0
lines changed

clippy_lints/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
513513
#[cfg(feature = "internal-lints")]
514514
&utils::internal_lints::INVALID_PATHS,
515515
#[cfg(feature = "internal-lints")]
516+
&utils::internal_lints::INTERNING_DEFINED_SYMBOL,
517+
#[cfg(feature = "internal-lints")]
516518
&utils::internal_lints::LINT_WITHOUT_LINT_PASS,
517519
#[cfg(feature = "internal-lints")]
518520
&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
@@ -958,6 +960,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
958960
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
959961
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
960962
store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
963+
store.register_late_pass(|| box utils::internal_lints::InterningDefinedSymbol::default());
961964
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
962965
store.register_late_pass(|| box utils::internal_lints::MatchTypeOnDiagItem);
963966
store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
@@ -1350,6 +1353,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
13501353
LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
13511354
LintId::of(&utils::internal_lints::DEFAULT_LINT),
13521355
LintId::of(&utils::internal_lints::INVALID_PATHS),
1356+
LintId::of(&utils::internal_lints::INTERNING_DEFINED_SYMBOL),
13531357
LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
13541358
LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
13551359
LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA),

clippy_lints/src/utils/internal_lints.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
1515
use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind};
1616
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
1717
use rustc_middle::hir::map::Map;
18+
use rustc_middle::mir::interpret::ConstValue;
1819
use rustc_middle::ty;
1920
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
2021
use rustc_span::source_map::{Span, Spanned};
@@ -247,6 +248,30 @@ declare_clippy_lint! {
247248
"invalid path"
248249
}
249250

251+
declare_clippy_lint! {
252+
/// **What it does:**
253+
/// Checks for interning symbols that have already been pre-interned and defined as constants.
254+
///
255+
/// **Why is this bad?**
256+
/// It's faster and easier to use the symbol constant.
257+
///
258+
/// **Known problems:** None.
259+
///
260+
/// **Example:**
261+
/// Bad:
262+
/// ```rust,ignore
263+
/// let _ = sym!(f32);
264+
/// ```
265+
///
266+
/// Good:
267+
/// ```rust,ignore
268+
/// let _ = sym::f32;
269+
/// ```
270+
pub INTERNING_DEFINED_SYMBOL,
271+
internal,
272+
"interning a symbol that is pre-interned and defined as a constant"
273+
}
274+
250275
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
251276

252277
impl EarlyLintPass for ClippyLintsInternal {
@@ -840,3 +865,58 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
840865
}
841866
}
842867
}
868+
869+
#[derive(Default)]
870+
pub struct InterningDefinedSymbol {
871+
// Maps the symbol to the constant name.
872+
symbol_map: FxHashMap<String, String>,
873+
}
874+
875+
impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL]);
876+
877+
impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
878+
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
879+
if !self.symbol_map.is_empty() {
880+
return;
881+
}
882+
883+
if let Some(Res::Def(_, def_id)) = path_to_res(cx, &paths::SYM_MODULE) {
884+
for item in cx.tcx.item_children(def_id).iter() {
885+
if_chain! {
886+
if let Res::Def(DefKind::Const, item_def_id) = item.res;
887+
let ty = cx.tcx.type_of(item_def_id);
888+
if match_type(cx, ty, &paths::SYMBOL);
889+
if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
890+
if let Ok(value) = value.to_u32();
891+
then {
892+
// SAFETY: We're converting the raw bytes of the symbol value back
893+
// into a Symbol instance.
894+
let symbol = unsafe { std::mem::transmute::<u32, Symbol>(value) };
895+
self.symbol_map.insert(symbol.to_string(), item.ident.to_string());
896+
}
897+
}
898+
}
899+
}
900+
}
901+
902+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
903+
if_chain! {
904+
if let ExprKind::Call(func, [arg]) = &expr.kind;
905+
if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
906+
if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
907+
if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
908+
if let Some(symbol_const) = self.symbol_map.get(&arg);
909+
then {
910+
span_lint_and_sugg(
911+
cx,
912+
INTERNING_DEFINED_SYMBOL,
913+
is_expn_of(expr.span, "sym").unwrap_or(expr.span),
914+
"interning a defined symbol",
915+
"try",
916+
format!("rustc_span::symbol::sym::{}", symbol_const),
917+
Applicability::MachineApplicable,
918+
);
919+
}
920+
}
921+
}
922+
}

clippy_lints/src/utils/paths.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
146146
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
147147
pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
148148
#[cfg(feature = "internal-lints")]
149+
pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
150+
#[cfg(feature = "internal-lints")]
151+
pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
152+
#[cfg(feature = "internal-lints")]
153+
pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
154+
#[cfg(feature = "internal-lints")]
149155
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
150156
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
151157
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// run-rustfix
2+
#![deny(clippy::internal)]
3+
#![feature(rustc_private)]
4+
5+
extern crate rustc_span;
6+
7+
use rustc_span::symbol::Symbol;
8+
9+
macro_rules! sym {
10+
($tt:tt) => {
11+
rustc_span::symbol::Symbol::intern(stringify!($tt))
12+
};
13+
}
14+
15+
fn main() {
16+
// Direct use of Symbol::intern
17+
let _ = rustc_span::symbol::sym::f32;
18+
19+
// Using a sym macro
20+
let _ = rustc_span::symbol::sym::f32;
21+
22+
// Correct suggestion when symbol isn't stringified constant name
23+
let _ = rustc_span::symbol::sym::proc_dash_macro;
24+
25+
// Interning a symbol that is not defined
26+
let _ = Symbol::intern("xyz123");
27+
let _ = sym!(xyz123);
28+
29+
// Using a different `intern` function
30+
let _ = intern("f32");
31+
}
32+
33+
fn intern(_: &str) {}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// run-rustfix
2+
#![deny(clippy::internal)]
3+
#![feature(rustc_private)]
4+
5+
extern crate rustc_span;
6+
7+
use rustc_span::symbol::Symbol;
8+
9+
macro_rules! sym {
10+
($tt:tt) => {
11+
rustc_span::symbol::Symbol::intern(stringify!($tt))
12+
};
13+
}
14+
15+
fn main() {
16+
// Direct use of Symbol::intern
17+
let _ = Symbol::intern("f32");
18+
19+
// Using a sym macro
20+
let _ = sym!(f32);
21+
22+
// Correct suggestion when symbol isn't stringified constant name
23+
let _ = Symbol::intern("proc-macro");
24+
25+
// Interning a symbol that is not defined
26+
let _ = Symbol::intern("xyz123");
27+
let _ = sym!(xyz123);
28+
29+
// Using a different `intern` function
30+
let _ = intern("f32");
31+
}
32+
33+
fn intern(_: &str) {}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error: interning a defined symbol
2+
--> $DIR/interning_defined_symbol.rs:17:13
3+
|
4+
LL | let _ = Symbol::intern("f32");
5+
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32`
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/interning_defined_symbol.rs:2:9
9+
|
10+
LL | #![deny(clippy::internal)]
11+
| ^^^^^^^^^^^^^^^^
12+
= note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]`
13+
14+
error: interning a defined symbol
15+
--> $DIR/interning_defined_symbol.rs:20:13
16+
|
17+
LL | let _ = sym!(f32);
18+
| ^^^^^^^^^ help: try: `rustc_span::symbol::sym::f32`
19+
20+
error: interning a defined symbol
21+
--> $DIR/interning_defined_symbol.rs:23:13
22+
|
23+
LL | let _ = Symbol::intern("proc-macro");
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::sym::proc_dash_macro`
25+
26+
error: aborting due to 3 previous errors
27+

0 commit comments

Comments
 (0)