@@ -15,6 +15,7 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
15
15
use rustc_hir:: { Crate , Expr , ExprKind , HirId , Item , MutTy , Mutability , Node , Path , StmtKind , Ty , TyKind } ;
16
16
use rustc_lint:: { EarlyContext , EarlyLintPass , LateContext , LateLintPass } ;
17
17
use rustc_middle:: hir:: map:: Map ;
18
+ use rustc_middle:: mir:: interpret:: ConstValue ;
18
19
use rustc_middle:: ty;
19
20
use rustc_session:: { declare_lint_pass, declare_tool_lint, impl_lint_pass} ;
20
21
use rustc_span:: source_map:: { Span , Spanned } ;
@@ -247,6 +248,30 @@ declare_clippy_lint! {
247
248
"invalid path"
248
249
}
249
250
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
+
250
275
declare_lint_pass ! ( ClippyLintsInternal => [ CLIPPY_LINTS_INTERNAL ] ) ;
251
276
252
277
impl EarlyLintPass for ClippyLintsInternal {
@@ -840,3 +865,56 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
840
865
}
841
866
}
842
867
}
868
+
869
+ #[ derive( Default ) ]
870
+ pub struct InterningDefinedSymbol {
871
+ // Maps the symbol value to the constant name.
872
+ symbol_map : FxHashMap < u32 , 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
+ self . symbol_map. insert( value, item. ident. to_string( ) ) ;
893
+ }
894
+ }
895
+ }
896
+ }
897
+ }
898
+
899
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
900
+ if_chain ! {
901
+ if let ExprKind :: Call ( func, [ arg] ) = & expr. kind;
902
+ if let ty:: FnDef ( def_id, _) = cx. typeck_results( ) . expr_ty( func) . kind( ) ;
903
+ if match_def_path( cx, * def_id, & paths:: SYMBOL_INTERN ) ;
904
+ if let Some ( Constant :: Str ( arg) ) = constant_simple( cx, cx. typeck_results( ) , arg) ;
905
+ let value = Symbol :: intern( & arg) . as_u32( ) ;
906
+ if let Some ( symbol_const) = self . symbol_map. get( & value) ;
907
+ then {
908
+ span_lint_and_sugg(
909
+ cx,
910
+ INTERNING_DEFINED_SYMBOL ,
911
+ is_expn_of( expr. span, "sym" ) . unwrap_or( expr. span) ,
912
+ "interning a defined symbol" ,
913
+ "try" ,
914
+ format!( "rustc_span::symbol::sym::{}" , symbol_const) ,
915
+ Applicability :: MachineApplicable ,
916
+ ) ;
917
+ }
918
+ }
919
+ }
920
+ }
0 commit comments