11use crate :: utils:: {
2- is_expn_of, match_def_path, match_qpath, match_type, method_calls, paths , run_lints , snippet , span_lint ,
3- span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq ,
2+ is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res , paths , qpath_res , run_lints ,
3+ snippet , span_lint , span_lint_and_help, span_lint_and_sugg, walk_ptrs_ty, SpanlessEq ,
44} ;
55use if_chain:: if_chain;
66use rustc_ast:: ast:: { Crate as AstCrate , ItemKind , LitKind , NodeId } ;
@@ -11,7 +11,7 @@ use rustc_hir as hir;
1111use rustc_hir:: def:: { DefKind , Res } ;
1212use rustc_hir:: hir_id:: CRATE_HIR_ID ;
1313use rustc_hir:: intravisit:: { NestedVisitorMap , Visitor } ;
14- use rustc_hir:: { Crate , Expr , ExprKind , HirId , Item , MutTy , Mutability , Path , StmtKind , Ty , TyKind } ;
14+ use rustc_hir:: { Crate , Expr , ExprKind , HirId , Item , MutTy , Mutability , Node , Path , StmtKind , Ty , TyKind } ;
1515use rustc_lint:: { EarlyContext , EarlyLintPass , LateContext , LateLintPass } ;
1616use rustc_middle:: hir:: map:: Map ;
1717use rustc_session:: { declare_lint_pass, declare_tool_lint, impl_lint_pass} ;
@@ -206,6 +206,29 @@ declare_clippy_lint! {
206206 "found collapsible `span_lint_and_then` calls"
207207}
208208
209+ declare_clippy_lint ! {
210+ /// **What it does:** Checks for calls to `utils::match_type()` on a type diagnostic item
211+ /// and suggests to use `utils::is_type_diagnostic_item()` instead.
212+ ///
213+ /// **Why is this bad?** `utils::is_type_diagnostic_item()` does not require hardcoded paths.
214+ ///
215+ /// **Known problems:** None.
216+ ///
217+ /// **Example:**
218+ /// Bad:
219+ /// ```rust,ignore
220+ /// utils::match_type(cx, ty, &paths::VEC)
221+ /// ```
222+ ///
223+ /// Good:
224+ /// ```rust,ignore
225+ /// utils::is_type_diagnostic_item(cx, ty, sym!(vec_type))
226+ /// ```
227+ pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM ,
228+ internal,
229+ "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
230+ }
231+
209232declare_lint_pass ! ( ClippyLintsInternal => [ CLIPPY_LINTS_INTERNAL ] ) ;
210233
211234impl EarlyLintPass for ClippyLintsInternal {
@@ -652,3 +675,89 @@ fn suggest_note(
652675 Applicability :: MachineApplicable ,
653676 ) ;
654677}
678+
679+ declare_lint_pass ! ( MatchTypeOnDiagItem => [ MATCH_TYPE_ON_DIAGNOSTIC_ITEM ] ) ;
680+
681+ impl < ' tcx > LateLintPass < ' tcx > for MatchTypeOnDiagItem {
682+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' _ > ) {
683+ if !run_lints ( cx, & [ MATCH_TYPE_ON_DIAGNOSTIC_ITEM ] , expr. hir_id ) {
684+ return ;
685+ }
686+
687+ if_chain ! {
688+ // Check if this is a call to utils::match_type()
689+ if let ExprKind :: Call ( fn_path, [ context, ty, ty_path] ) = expr. kind;
690+ if let ExprKind :: Path ( fn_qpath) = & fn_path. kind;
691+ if match_qpath( & fn_qpath, & [ "utils" , "match_type" ] ) ;
692+ // Extract the path to the matched type
693+ if let Some ( segments) = path_to_matched_type( cx, ty_path) ;
694+ let segments: Vec <& str > = segments. iter( ) . map( |sym| & * * sym) . collect( ) ;
695+ if let Some ( ty_did) = path_to_res( cx, & segments[ ..] ) . and_then( |res| res. opt_def_id( ) ) ;
696+ // Check if the matched type is a diagnostic item
697+ let diag_items = cx. tcx. diagnostic_items( ty_did. krate) ;
698+ if let Some ( item_name) = diag_items. iter( ) . find_map( |( k, v) | if * v == ty_did { Some ( k) } else { None } ) ;
699+ then {
700+ let cx_snippet = snippet( cx, context. span, "_" ) ;
701+ let ty_snippet = snippet( cx, ty. span, "_" ) ;
702+
703+ span_lint_and_sugg(
704+ cx,
705+ MATCH_TYPE_ON_DIAGNOSTIC_ITEM ,
706+ expr. span,
707+ "usage of `utils::match_type()` on a type diagnostic item" ,
708+ "try" ,
709+ format!( "utils::is_type_diagnostic_item({}, {}, sym!({}))" , cx_snippet, ty_snippet, item_name) ,
710+ Applicability :: MaybeIncorrect ,
711+ ) ;
712+ }
713+ }
714+ }
715+ }
716+
717+ fn path_to_matched_type ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) -> Option < Vec < SymbolStr > > {
718+ use rustc_hir:: ItemKind ;
719+
720+ match & expr. kind {
721+ ExprKind :: AddrOf ( .., expr) => return path_to_matched_type ( cx, expr) ,
722+ ExprKind :: Path ( qpath) => match qpath_res ( cx, qpath, expr. hir_id ) {
723+ Res :: Local ( hir_id) => {
724+ let parent_id = cx. tcx . hir ( ) . get_parent_node ( hir_id) ;
725+ if let Some ( Node :: Local ( local) ) = cx. tcx . hir ( ) . find ( parent_id) {
726+ if let Some ( init) = local. init {
727+ return path_to_matched_type ( cx, init) ;
728+ }
729+ }
730+ } ,
731+ Res :: Def ( DefKind :: Const | DefKind :: Static , def_id) => {
732+ if let Some ( Node :: Item ( item) ) = cx. tcx . hir ( ) . get_if_local ( def_id) {
733+ if let ItemKind :: Const ( .., body_id) | ItemKind :: Static ( .., body_id) = item. kind {
734+ let body = cx. tcx . hir ( ) . body ( body_id) ;
735+ return path_to_matched_type ( cx, & body. value ) ;
736+ }
737+ }
738+ } ,
739+ _ => { } ,
740+ } ,
741+ ExprKind :: Array ( exprs) => {
742+ let segments: Vec < SymbolStr > = exprs
743+ . iter ( )
744+ . filter_map ( |expr| {
745+ if let ExprKind :: Lit ( lit) = & expr. kind {
746+ if let LitKind :: Str ( sym, _) = lit. node {
747+ return Some ( sym. as_str ( ) ) ;
748+ }
749+ }
750+
751+ None
752+ } )
753+ . collect ( ) ;
754+
755+ if segments. len ( ) == exprs. len ( ) {
756+ return Some ( segments) ;
757+ }
758+ } ,
759+ _ => { } ,
760+ }
761+
762+ None
763+ }
0 commit comments