|
| 1 | +use clippy_config::Conf; |
| 2 | +use clippy_config::types::{DisallowedPath, create_disallowed_map}; |
| 3 | +use clippy_utils::diagnostics::span_lint_and_then; |
| 4 | +use clippy_utils::paths::PathNS; |
| 5 | +use rustc_hir::def::{DefKind, Res}; |
| 6 | +use rustc_hir::def_id::DefIdMap; |
| 7 | +use rustc_hir::{Expr, ExprKind}; |
| 8 | +use rustc_lint::{LateContext, LateLintPass}; |
| 9 | +use rustc_middle::ty::{Adt, TyCtxt}; |
| 10 | +use rustc_session::impl_lint_pass; |
| 11 | + |
| 12 | +declare_clippy_lint! { |
| 13 | + /// ### What it does |
| 14 | + /// Denies the configured fields in clippy.toml |
| 15 | + /// |
| 16 | + /// Note: Even though this lint is warn-by-default, it will only trigger if |
| 17 | + /// fields are defined in the clippy.toml file. |
| 18 | + /// |
| 19 | + /// ### Why is this bad? |
| 20 | + /// Some fields are undesirable in certain contexts, and it's beneficial to |
| 21 | + /// lint for them as needed. |
| 22 | + /// |
| 23 | + /// ### Example |
| 24 | + /// An example clippy.toml configuration: |
| 25 | + /// ```toml |
| 26 | + /// # clippy.toml |
| 27 | + /// disallowed-fields = [ |
| 28 | + /// # Can use a string as the path of the disallowed field. |
| 29 | + /// "std::ops::Range::start", |
| 30 | + /// # Can also use an inline table with a `path` key. |
| 31 | + /// { path = "std::ops::Range::start" }, |
| 32 | + /// # When using an inline table, can add a `reason` for why the field |
| 33 | + /// # is disallowed. |
| 34 | + /// { path = "std::ops::Range::start", reason = "The start of the range is not used" }, |
| 35 | + /// ] |
| 36 | + /// ``` |
| 37 | + /// |
| 38 | + /// ```rust |
| 39 | + /// use std::ops::Range; |
| 40 | + /// |
| 41 | + /// let range = Range { start: 0, end: 1 }; |
| 42 | + /// println!("{}", range.start); // `start` is disallowed in the config. |
| 43 | + /// ``` |
| 44 | + /// |
| 45 | + /// Use instead: |
| 46 | + /// ```rust |
| 47 | + /// use std::ops::Range; |
| 48 | + /// |
| 49 | + /// let range = Range { start: 0, end: 1 }; |
| 50 | + /// println!("{}", range.end); // `end` is _not_ disallowed in the config. |
| 51 | + /// ``` |
| 52 | + #[clippy::version = "1.93.0"] |
| 53 | + pub DISALLOWED_FIELDS, |
| 54 | + style, |
| 55 | + "declaration of a disallowed field use" |
| 56 | +} |
| 57 | + |
| 58 | +pub struct DisallowedFields { |
| 59 | + disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, |
| 60 | +} |
| 61 | + |
| 62 | +impl DisallowedFields { |
| 63 | + pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { |
| 64 | + let (disallowed, _) = create_disallowed_map( |
| 65 | + tcx, |
| 66 | + &conf.disallowed_fields, |
| 67 | + PathNS::Value, |
| 68 | + |def_kind| matches!(def_kind, DefKind::Field), |
| 69 | + "field", |
| 70 | + false, |
| 71 | + ); |
| 72 | + Self { disallowed } |
| 73 | + } |
| 74 | +} |
| 75 | + |
| 76 | +impl_lint_pass!(DisallowedFields => [DISALLOWED_FIELDS]); |
| 77 | + |
| 78 | +impl<'tcx> LateLintPass<'tcx> for DisallowedFields { |
| 79 | + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { |
| 80 | + let (id, span) = match &expr.kind { |
| 81 | + ExprKind::Path(path) if let Res::Def(_, id) = cx.qpath_res(path, expr.hir_id) => (id, expr.span), |
| 82 | + ExprKind::Field(e, ident) => { |
| 83 | + // Very round-about way to get the field `DefId` from the expr: first we get its |
| 84 | + // parent `Ty`. Then we go through all its fields to find the one with the expected |
| 85 | + // name and get the `DefId` from it. |
| 86 | + if let Some(parent_ty) = cx.typeck_results().expr_ty_opt(e) |
| 87 | + && let Adt(adt_def, ..) = parent_ty.kind() |
| 88 | + && let Some(field_def_id) = adt_def.all_fields().find_map(|field| { |
| 89 | + if field.name == ident.name { |
| 90 | + Some(field.did) |
| 91 | + } else { |
| 92 | + None |
| 93 | + } |
| 94 | + }) |
| 95 | + { |
| 96 | + (field_def_id, ident.span) |
| 97 | + } else { |
| 98 | + return; |
| 99 | + } |
| 100 | + }, |
| 101 | + _ => return, |
| 102 | + }; |
| 103 | + if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) { |
| 104 | + span_lint_and_then( |
| 105 | + cx, |
| 106 | + DISALLOWED_FIELDS, |
| 107 | + span, |
| 108 | + format!("use of a disallowed field `{path}`"), |
| 109 | + disallowed_path.diag_amendment(span), |
| 110 | + ); |
| 111 | + } |
| 112 | + } |
| 113 | +} |
0 commit comments