]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
Rollup merge of #104901 - krtab:filetype_compare, r=the8472
[rust.git] / src / tools / clippy / clippy_lints / src / functions / misnamed_getters.rs
1 use clippy_utils::diagnostics::span_lint_and_then;
2 use clippy_utils::source::snippet;
3 use rustc_errors::Applicability;
4 use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, ImplicitSelfKind, Unsafety};
5 use rustc_lint::LateContext;
6 use rustc_middle::ty;
7 use rustc_span::Span;
8
9 use std::iter;
10
11 use super::MISNAMED_GETTERS;
12
13 pub fn check_fn(
14     cx: &LateContext<'_>,
15     kind: FnKind<'_>,
16     decl: &FnDecl<'_>,
17     body: &Body<'_>,
18     span: Span,
19     _hir_id: HirId,
20 ) {
21     let FnKind::Method(ref ident, sig) = kind else {
22             return;
23         };
24
25     // Takes only &(mut) self
26     if decl.inputs.len() != 1 {
27         return;
28     }
29
30     let name = ident.name.as_str();
31
32     let name = match decl.implicit_self {
33         ImplicitSelfKind::MutRef => {
34             let Some(name) = name.strip_suffix("_mut") else {
35                     return;
36                 };
37             name
38         },
39         ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name,
40         ImplicitSelfKind::None => return,
41     };
42
43     let name = if sig.header.unsafety == Unsafety::Unsafe {
44         name.strip_suffix("_unchecked").unwrap_or(name)
45     } else {
46         name
47     };
48
49     // Body must be &(mut) <self_data>.name
50     // self_data is not neccessarilly self, to also lint sub-getters, etc…
51
52     let block_expr = if_chain! {
53         if let ExprKind::Block(block,_) = body.value.kind;
54         if block.stmts.is_empty();
55         if let Some(block_expr) = block.expr;
56         then {
57             block_expr
58         } else {
59             return;
60         }
61     };
62     let expr_span = block_expr.span;
63
64     // Accept &<expr>, &mut <expr> and <expr>
65     let expr = if let ExprKind::AddrOf(_, _, tmp) = block_expr.kind {
66         tmp
67     } else {
68         block_expr
69     };
70     let (self_data, used_ident) = if_chain! {
71         if let ExprKind::Field(self_data, ident) = expr.kind;
72         if ident.name.as_str() != name;
73         then {
74             (self_data, ident)
75         } else {
76             return;
77         }
78     };
79
80     let mut used_field = None;
81     let mut correct_field = None;
82     let typeck_results = cx.typeck_results();
83     for adjusted_type in iter::once(typeck_results.expr_ty(self_data))
84         .chain(typeck_results.expr_adjustments(self_data).iter().map(|adj| adj.target))
85     {
86         let ty::Adt(def,_) = adjusted_type.kind() else {
87             continue;
88         };
89
90         for f in def.all_fields() {
91             if f.name.as_str() == name {
92                 correct_field = Some(f);
93             }
94             if f.name == used_ident.name {
95                 used_field = Some(f);
96             }
97         }
98     }
99
100     let Some(used_field) = used_field else {
101         // Can happen if the field access is a tuple. We don't lint those because the getter name could not start with a number.
102         return;
103     };
104
105     let Some(correct_field) = correct_field else {
106         // There is no field corresponding to the getter name.
107         // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is
108         return;
109     };
110
111     if cx.tcx.type_of(used_field.did) == cx.tcx.type_of(correct_field.did) {
112         let left_span = block_expr.span.until(used_ident.span);
113         let snippet = snippet(cx, left_span, "..");
114         let sugg = format!("{snippet}{name}");
115         span_lint_and_then(
116             cx,
117             MISNAMED_GETTERS,
118             span,
119             "getter function appears to return the wrong field",
120             |diag| {
121                 diag.span_suggestion(expr_span, "consider using", sugg, Applicability::MaybeIncorrect);
122             },
123         );
124     }
125 }