]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
New upstream version 1.67.1+dfsg1
[rustc.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 }