]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/utils/internal_lints/invalid_paths.rs
Auto merge of #9648 - llogiq:fix-undocumented-unsafe-blocks, r=Jarcho
[rust.git] / clippy_lints / src / utils / internal_lints / invalid_paths.rs
1 use clippy_utils::consts::{constant_simple, Constant};
2 use clippy_utils::def_path_res;
3 use clippy_utils::diagnostics::span_lint;
4 use if_chain::if_chain;
5 use rustc_hir as hir;
6 use rustc_hir::def::{DefKind, Res};
7 use rustc_hir::Item;
8 use rustc_hir_analysis::hir_ty_to_ty;
9 use rustc_lint::{LateContext, LateLintPass};
10 use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, FloatTy};
11 use rustc_session::{declare_lint_pass, declare_tool_lint};
12 use rustc_span::symbol::Symbol;
13
14 declare_clippy_lint! {
15     /// ### What it does
16     /// Checks the paths module for invalid paths.
17     ///
18     /// ### Why is this bad?
19     /// It indicates a bug in the code.
20     ///
21     /// ### Example
22     /// None.
23     pub INVALID_PATHS,
24     internal,
25     "invalid path"
26 }
27
28 declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
29
30 impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
31     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
32         let local_def_id = &cx.tcx.parent_module(item.hir_id());
33         let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
34         if_chain! {
35             if mod_name.as_str() == "paths";
36             if let hir::ItemKind::Const(ty, body_id) = item.kind;
37             let ty = hir_ty_to_ty(cx.tcx, ty);
38             if let ty::Array(el_ty, _) = &ty.kind();
39             if let ty::Ref(_, el_ty, _) = &el_ty.kind();
40             if el_ty.is_str();
41             let body = cx.tcx.hir().body(body_id);
42             let typeck_results = cx.tcx.typeck_body(body_id);
43             if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value);
44             let path: Vec<&str> = path
45                 .iter()
46                 .map(|x| {
47                     if let Constant::Str(s) = x {
48                         s.as_str()
49                     } else {
50                         // We checked the type of the constant above
51                         unreachable!()
52                     }
53                 })
54                 .collect();
55             if !check_path(cx, &path[..]);
56             then {
57                 span_lint(cx, INVALID_PATHS, item.span, "invalid path");
58             }
59         }
60     }
61 }
62
63 // This is not a complete resolver for paths. It works on all the paths currently used in the paths
64 // module.  That's all it does and all it needs to do.
65 pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
66     if def_path_res(cx, path, None) != Res::Err {
67         return true;
68     }
69
70     // Some implementations can't be found by `path_to_res`, particularly inherent
71     // implementations of native types. Check lang items.
72     let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
73     let lang_items = cx.tcx.lang_items();
74     // This list isn't complete, but good enough for our current list of paths.
75     let incoherent_impls = [
76         SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32),
77         SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64),
78         SimplifiedTypeGen::SliceSimplifiedType,
79         SimplifiedTypeGen::StrSimplifiedType,
80     ]
81     .iter()
82     .flat_map(|&ty| cx.tcx.incoherent_impls(ty));
83     for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) {
84         let lang_item_path = cx.get_def_path(*item_def_id);
85         if path_syms.starts_with(&lang_item_path) {
86             if let [item] = &path_syms[lang_item_path.len()..] {
87                 if matches!(
88                     cx.tcx.def_kind(*item_def_id),
89                     DefKind::Mod | DefKind::Enum | DefKind::Trait
90                 ) {
91                     for child in cx.tcx.module_children(*item_def_id) {
92                         if child.ident.name == *item {
93                             return true;
94                         }
95                     }
96                 } else {
97                     for child in cx.tcx.associated_item_def_ids(*item_def_id) {
98                         if cx.tcx.item_name(*child) == *item {
99                             return true;
100                         }
101                     }
102                 }
103             }
104         }
105     }
106
107     false
108 }