1
1
use 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 ,
4
4
} ;
5
5
use if_chain:: if_chain;
6
6
use rustc_ast:: ast:: { Crate as AstCrate , ItemKind , LitKind , NodeId } ;
@@ -11,7 +11,7 @@ use rustc_hir as hir;
11
11
use rustc_hir:: def:: { DefKind , Res } ;
12
12
use rustc_hir:: hir_id:: CRATE_HIR_ID ;
13
13
use 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 } ;
15
15
use rustc_lint:: { EarlyContext , EarlyLintPass , LateContext , LateLintPass } ;
16
16
use rustc_middle:: hir:: map:: Map ;
17
17
use rustc_session:: { declare_lint_pass, declare_tool_lint, impl_lint_pass} ;
@@ -206,6 +206,29 @@ declare_clippy_lint! {
206
206
"found collapsible `span_lint_and_then` calls"
207
207
}
208
208
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
+
209
232
declare_lint_pass ! ( ClippyLintsInternal => [ CLIPPY_LINTS_INTERNAL ] ) ;
210
233
211
234
impl EarlyLintPass for ClippyLintsInternal {
@@ -652,3 +675,89 @@ fn suggest_note(
652
675
Applicability :: MachineApplicable ,
653
676
) ;
654
677
}
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