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