@@ -15,6 +15,7 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
1515use rustc_hir:: { Crate , Expr , ExprKind , HirId , Item , MutTy , Mutability , Node , Path , StmtKind , Ty , TyKind } ;
1616use rustc_lint:: { EarlyContext , EarlyLintPass , LateContext , LateLintPass } ;
1717use rustc_middle:: hir:: map:: Map ;
18+ use rustc_middle:: mir:: interpret:: ConstValue ;
1819use rustc_middle:: ty;
1920use rustc_session:: { declare_lint_pass, declare_tool_lint, impl_lint_pass} ;
2021use 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+
250275declare_lint_pass ! ( ClippyLintsInternal => [ CLIPPY_LINTS_INTERNAL ] ) ;
251276
252277impl EarlyLintPass for ClippyLintsInternal {
@@ -840,3 +865,56 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
840865 }
841866 }
842867}
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