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
;
11 use super::MISNAMED_GETTERS
;
21 let FnKind
::Method(ref ident
, sig
) = kind
else {
25 // Takes only &(mut) self
26 if decl
.inputs
.len() != 1 {
30 let name
= ident
.name
.as_str();
32 let name
= match decl
.implicit_self
{
33 ImplicitSelfKind
::MutRef
=> {
34 let Some(name
) = name
.strip_suffix("_mut") else {
39 ImplicitSelfKind
::Imm
| ImplicitSelfKind
::Mut
| ImplicitSelfKind
::ImmRef
=> name
,
40 ImplicitSelfKind
::None
=> return,
43 let name
= if sig
.header
.unsafety
== Unsafety
::Unsafe
{
44 name
.strip_suffix("_unchecked").unwrap_or(name
)
49 // Body must be &(mut) <self_data>.name
50 // self_data is not neccessarilly self, to also lint sub-getters, etc…
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
;
62 let expr_span
= block_expr
.span
;
64 // Accept &<expr>, &mut <expr> and <expr>
65 let expr
= if let ExprKind
::AddrOf(_
, _
, tmp
) = block_expr
.kind
{
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
;
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
))
86 let ty
::Adt(def
,_
) = adjusted_type
.kind() else {
90 for f
in def
.all_fields() {
91 if f
.name
.as_str() == name
{
92 correct_field
= Some(f
);
94 if f
.name
== used_ident
.name
{
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.
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
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}");
119 "getter function appears to return the wrong field",
121 diag
.span_suggestion(expr_span
, "consider using", sugg
, Applicability
::MaybeIncorrect
);