1+ use crate :: consts:: { constant_simple, Constant } ;
12use crate :: utils:: {
23 is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints,
34 snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq ,
@@ -14,9 +15,11 @@ use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
1415use rustc_hir:: { Crate , Expr , ExprKind , HirId , Item , MutTy , Mutability , Node , Path , StmtKind , Ty , TyKind } ;
1516use rustc_lint:: { EarlyContext , EarlyLintPass , LateContext , LateLintPass } ;
1617use rustc_middle:: hir:: map:: Map ;
18+ use rustc_middle:: ty;
1719use rustc_session:: { declare_lint_pass, declare_tool_lint, impl_lint_pass} ;
1820use rustc_span:: source_map:: { Span , Spanned } ;
1921use rustc_span:: symbol:: { Symbol , SymbolStr } ;
22+ use rustc_typeck:: hir_ty_to_ty;
2023
2124use std:: borrow:: { Borrow , Cow } ;
2225
@@ -229,6 +232,21 @@ declare_clippy_lint! {
229232 "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
230233}
231234
235+ declare_clippy_lint ! {
236+ /// **What it does:**
237+ /// Checks the paths module for invalid paths.
238+ ///
239+ /// **Why is this bad?**
240+ /// It indicates a bug in the code.
241+ ///
242+ /// **Known problems:** None.
243+ ///
244+ /// **Example:** None.
245+ pub INVALID_PATHS ,
246+ internal,
247+ "invalid path"
248+ }
249+
232250declare_lint_pass ! ( ClippyLintsInternal => [ CLIPPY_LINTS_INTERNAL ] ) ;
233251
234252impl EarlyLintPass for ClippyLintsInternal {
@@ -761,3 +779,64 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
761779
762780 None
763781}
782+
783+ // This is not a complete resolver for paths. It works on all the paths currently used in the paths
784+ // module. That's all it does and all it needs to do.
785+ pub fn check_path ( cx : & LateContext < ' _ > , path : & [ & str ] ) -> bool {
786+ if path_to_res ( cx, path) . is_some ( ) {
787+ return true ;
788+ }
789+
790+ // Some implementations can't be found by `path_to_res`, particularly inherent
791+ // implementations of native types. Check lang items.
792+ let path_syms: Vec < _ > = path. iter ( ) . map ( |p| Symbol :: intern ( p) ) . collect ( ) ;
793+ let lang_items = cx. tcx . lang_items ( ) ;
794+ for lang_item in lang_items. items ( ) {
795+ if let Some ( def_id) = lang_item {
796+ let lang_item_path = cx. get_def_path ( * def_id) ;
797+ if path_syms. starts_with ( & lang_item_path) {
798+ if let [ item] = & path_syms[ lang_item_path. len ( ) ..] {
799+ for child in cx. tcx . item_children ( * def_id) {
800+ if child. ident . name == * item {
801+ return true ;
802+ }
803+ }
804+ }
805+ }
806+ }
807+ }
808+
809+ false
810+ }
811+
812+ declare_lint_pass ! ( InvalidPaths => [ INVALID_PATHS ] ) ;
813+
814+ impl < ' tcx > LateLintPass < ' tcx > for InvalidPaths {
815+ fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
816+ let local_def_id = & cx. tcx . parent_module ( item. hir_id ) ;
817+ let mod_name = & cx. tcx . item_name ( local_def_id. to_def_id ( ) ) ;
818+ if_chain ! {
819+ if mod_name. as_str( ) == "paths" ;
820+ if let hir:: ItemKind :: Const ( ty, body_id) = item. kind;
821+ let ty = hir_ty_to_ty( cx. tcx, ty) ;
822+ if let ty:: Array ( el_ty, _) = & ty. kind( ) ;
823+ if let ty:: Ref ( _, el_ty, _) = & el_ty. kind( ) ;
824+ if el_ty. is_str( ) ;
825+ let body = cx. tcx. hir( ) . body( body_id) ;
826+ let typeck_results = cx. tcx. typeck_body( body_id) ;
827+ if let Some ( Constant :: Vec ( path) ) = constant_simple( cx, typeck_results, & body. value) ;
828+ let path: Vec <& str > = path. iter( ) . map( |x| {
829+ if let Constant :: Str ( s) = x {
830+ s. as_str( )
831+ } else {
832+ // We checked the type of the constant above
833+ unreachable!( )
834+ }
835+ } ) . collect( ) ;
836+ if !check_path( cx, & path[ ..] ) ;
837+ then {
838+ span_lint( cx, CLIPPY_LINTS_INTERNAL , item. span, "invalid path" ) ;
839+ }
840+ }
841+ }
842+ }
0 commit comments