]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/disallowed_type.rs
Rollup merge of #89558 - lcnr:query-stable-lint, r=estebank
[rust.git] / src / tools / clippy / clippy_lints / src / disallowed_type.rs
1 use clippy_utils::diagnostics::span_lint_and_then;
2
3 use rustc_data_structures::fx::FxHashMap;
4 use rustc_hir::{
5     def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, TraitBoundModifier, Ty, TyKind, UseKind,
6 };
7 use rustc_lint::{LateContext, LateLintPass};
8 use rustc_session::{declare_tool_lint, impl_lint_pass};
9 use rustc_span::Span;
10
11 use crate::utils::conf;
12
13 declare_clippy_lint! {
14     /// ### What it does
15     /// Denies the configured types in clippy.toml.
16     ///
17     /// ### Why is this bad?
18     /// Some types are undesirable in certain contexts.
19     ///
20     /// ### Example:
21     /// An example clippy.toml configuration:
22     /// ```toml
23     /// # clippy.toml
24     /// disallowed-types = [
25     ///     # Can use a string as the path of the disallowed type.
26     ///     "std::collections::BTreeMap",
27     ///     # Can also use an inline table with a `path` key.
28     ///     { path = "std::net::TcpListener" },
29     ///     # When using an inline table, can add a `reason` for why the type
30     ///     # is disallowed.
31     ///     { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
32     /// ]
33     /// ```
34     ///
35     /// ```rust,ignore
36     /// use std::collections::BTreeMap;
37     /// // or its use
38     /// let x = std::collections::BTreeMap::new();
39     /// ```
40     /// Use instead:
41     /// ```rust,ignore
42     /// // A similar type that is allowed by the config
43     /// use std::collections::HashMap;
44     /// ```
45     pub DISALLOWED_TYPE,
46     nursery,
47     "use of a disallowed type"
48 }
49 #[derive(Clone, Debug)]
50 pub struct DisallowedType {
51     conf_disallowed: Vec<conf::DisallowedType>,
52     def_ids: FxHashMap<DefId, Option<String>>,
53     prim_tys: FxHashMap<PrimTy, Option<String>>,
54 }
55
56 impl DisallowedType {
57     pub fn new(conf_disallowed: Vec<conf::DisallowedType>) -> Self {
58         Self {
59             conf_disallowed,
60             def_ids: FxHashMap::default(),
61             prim_tys: FxHashMap::default(),
62         }
63     }
64
65     fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
66         match res {
67             Res::Def(_, did) => {
68                 if let Some(reason) = self.def_ids.get(did) {
69                     emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref());
70                 }
71             },
72             Res::PrimTy(prim) => {
73                 if let Some(reason) = self.prim_tys.get(prim) {
74                     emit(cx, prim.name_str(), span, reason.as_deref());
75                 }
76             },
77             _ => {},
78         }
79     }
80 }
81
82 impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);
83
84 impl<'tcx> LateLintPass<'tcx> for DisallowedType {
85     fn check_crate(&mut self, cx: &LateContext<'_>) {
86         for conf in &self.conf_disallowed {
87             let (path, reason) = match conf {
88                 conf::DisallowedType::Simple(path) => (path, None),
89                 conf::DisallowedType::WithReason { path, reason } => (
90                     path,
91                     reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
92                 ),
93             };
94             let segs: Vec<_> = path.split("::").collect();
95             match clippy_utils::path_to_res(cx, &segs) {
96                 Res::Def(_, id) => {
97                     self.def_ids.insert(id, reason);
98                 },
99                 Res::PrimTy(ty) => {
100                     self.prim_tys.insert(ty, reason);
101                 },
102                 _ => {},
103             }
104         }
105     }
106
107     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
108         if let ItemKind::Use(path, UseKind::Single) = &item.kind {
109             self.check_res_emit(cx, &path.res, item.span);
110         }
111     }
112
113     fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
114         if let TyKind::Path(path) = &ty.kind {
115             self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span);
116         }
117     }
118
119     fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
120         self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span);
121     }
122 }
123
124 fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
125     span_lint_and_then(
126         cx,
127         DISALLOWED_TYPE,
128         span,
129         &format!("`{}` is not allowed according to config", name),
130         |diag| {
131             if let Some(reason) = reason {
132                 diag.note(reason);
133             }
134         },
135     );
136 }