|
| 1 | +use clippy_utils::diagnostics::span_lint_and_help; |
| 2 | +use rustc_hir::def_id::DefId; |
| 3 | +use rustc_hir::hir_id::OwnerId; |
| 4 | +use rustc_hir::{ImplItem, ImplItemKind, ItemKind, Node}; |
| 5 | +use rustc_lint::LateContext; |
| 6 | +use rustc_span::symbol::{Ident, Symbol}; |
| 7 | +use rustc_span::Span; |
| 8 | + |
| 9 | +use super::RENAMED_FUNCTION_PARAMS; |
| 10 | + |
| 11 | +pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>) { |
| 12 | + if let ImplItemKind::Fn(_, body_id) = item.kind && |
| 13 | + let Some(did) = impled_item_def_id(cx, item.owner_id) |
| 14 | + { |
| 15 | + let mut param_idents_iter = cx.tcx.hir().body_param_names(body_id); |
| 16 | + let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied(); |
| 17 | + |
| 18 | + let renames = renamed_params(&mut default_param_idents_iter, &mut param_idents_iter); |
| 19 | + // FIXME: Should we use `MultiSpan` to combine output together? |
| 20 | + // But how should we display help message if so. |
| 21 | + for rename in renames { |
| 22 | + span_lint_and_help( |
| 23 | + cx, |
| 24 | + RENAMED_FUNCTION_PARAMS, |
| 25 | + rename.renamed_span, |
| 26 | + "function parameter name was renamed from its trait default", |
| 27 | + None, |
| 28 | + &format!("consider changing the name to: '{}'", rename.default_name.as_str()) |
| 29 | + ); |
| 30 | + } |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +struct RenamedParam { |
| 35 | + renamed_span: Span, |
| 36 | + default_name: Symbol, |
| 37 | +} |
| 38 | + |
| 39 | +fn renamed_params<I, T>(default_names: &mut I, current_names: &mut T) -> Vec<RenamedParam> |
| 40 | +where |
| 41 | + I: Iterator<Item = Ident>, |
| 42 | + T: Iterator<Item = Ident>, |
| 43 | +{ |
| 44 | + let mut renamed = vec![]; |
| 45 | + // FIXME: Should we stop if they have different length? |
| 46 | + while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) { |
| 47 | + let current_name = cur_name.name; |
| 48 | + let default_name = def_name.name; |
| 49 | + if is_ignored_or_empty_symbol(current_name) || is_ignored_or_empty_symbol(default_name) { |
| 50 | + continue; |
| 51 | + } |
| 52 | + if current_name != default_name { |
| 53 | + renamed.push(RenamedParam { |
| 54 | + renamed_span: cur_name.span, |
| 55 | + default_name, |
| 56 | + }); |
| 57 | + } |
| 58 | + } |
| 59 | + renamed |
| 60 | +} |
| 61 | + |
| 62 | +fn is_ignored_or_empty_symbol(symbol: Symbol) -> bool { |
| 63 | + let s = symbol.as_str(); |
| 64 | + s.is_empty() || s.starts_with('_') |
| 65 | +} |
| 66 | + |
| 67 | +fn impled_item_def_id(cx: &LateContext<'_>, impl_item_id: OwnerId) -> Option<DefId> { |
| 68 | + let trait_node = cx.tcx.hir().find_parent(impl_item_id.into())?; |
| 69 | + if let Node::Item(item) = trait_node && |
| 70 | + let ItemKind::Impl(impl_) = &item.kind |
| 71 | + { |
| 72 | + impl_.items.iter().find_map(|item| { |
| 73 | + if item.id.owner_id == impl_item_id { |
| 74 | + item.trait_item_def_id |
| 75 | + } else { |
| 76 | + None |
| 77 | + } |
| 78 | + }) |
| 79 | + } else { |
| 80 | + None |
| 81 | + } |
| 82 | +} |
0 commit comments