|
1 | 1 | use clippy_utils::diagnostics::span_lint_and_then;
|
2 | 2 | use clippy_utils::is_in_test_function;
|
3 | 3 |
|
| 4 | +use rustc_hir as hir; |
4 | 5 | use rustc_hir::intravisit::FnKind;
|
5 |
| -use rustc_hir::{Body, HirId}; |
| 6 | +use rustc_hir::{Body, GenericParam, Generics, HirId, ImplItem, ImplItemKind, TraitItem, TraitItemKind}; |
6 | 7 | use rustc_lint::LateContext;
|
7 |
| -use rustc_span::Span; |
| 8 | +use rustc_span::symbol::Ident; |
| 9 | +use rustc_span::{BytePos, Span}; |
8 | 10 |
|
9 | 11 | use super::IMPL_TRAIT_IN_PARAMS;
|
10 | 12 |
|
| 13 | +fn report( |
| 14 | + cx: &LateContext<'_>, |
| 15 | + param: &GenericParam<'_>, |
| 16 | + ident: &Ident, |
| 17 | + generics: &Generics<'_>, |
| 18 | + first_param_span: Span, |
| 19 | +) { |
| 20 | + // No generics with nested generics, and no generics like FnMut(x) |
| 21 | + span_lint_and_then( |
| 22 | + cx, |
| 23 | + IMPL_TRAIT_IN_PARAMS, |
| 24 | + param.span, |
| 25 | + "`impl Trait` used as a function parameter", |
| 26 | + |diag| { |
| 27 | + if let Some(gen_span) = generics.span_for_param_suggestion() { |
| 28 | + // If there's already a generic param with the same bound, do not lint **this** suggestion. |
| 29 | + diag.span_suggestion_with_style( |
| 30 | + gen_span, |
| 31 | + "add a type parameter", |
| 32 | + format!(", {{ /* Generic name */ }}: {}", ¶m.name.ident().as_str()[5..]), |
| 33 | + rustc_errors::Applicability::HasPlaceholders, |
| 34 | + rustc_errors::SuggestionStyle::ShowAlways, |
| 35 | + ); |
| 36 | + } else { |
| 37 | + diag.span_suggestion_with_style( |
| 38 | + Span::new( |
| 39 | + first_param_span.lo() - rustc_span::BytePos(1), |
| 40 | + ident.span.hi(), |
| 41 | + ident.span.ctxt(), |
| 42 | + ident.span.parent(), |
| 43 | + ), |
| 44 | + "add a type parameter", |
| 45 | + format!("<{{ /* Generic name */ }}: {}>", ¶m.name.ident().as_str()[5..]), |
| 46 | + rustc_errors::Applicability::HasPlaceholders, |
| 47 | + rustc_errors::SuggestionStyle::ShowAlways, |
| 48 | + ); |
| 49 | + } |
| 50 | + }, |
| 51 | + ); |
| 52 | +} |
| 53 | + |
11 | 54 | pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) {
|
12 |
| - if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public() && !is_in_test_function(cx.tcx, hir_id) |
13 |
| - { |
14 |
| - if let FnKind::ItemFn(ident, generics, _) = kind { |
| 55 | + if_chain! { |
| 56 | + if let FnKind::ItemFn(ident, generics, _) = kind; |
| 57 | + if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public(); |
| 58 | + if !is_in_test_function(cx.tcx, hir_id); |
| 59 | + then { |
15 | 60 | for param in generics.params {
|
16 | 61 | if param.is_impl_trait() {
|
17 |
| - // No generics with nested generics, and no generics like FnMut(x) |
18 |
| - span_lint_and_then( |
19 |
| - cx, |
20 |
| - IMPL_TRAIT_IN_PARAMS, |
21 |
| - param.span, |
22 |
| - "'`impl Trait` used as a function parameter'", |
23 |
| - |diag| { |
24 |
| - if let Some(gen_span) = generics.span_for_param_suggestion() { |
25 |
| - diag.span_suggestion_with_style( |
26 |
| - gen_span, |
27 |
| - "add a type parameter", |
28 |
| - format!(", {{ /* Generic name */ }}: {}", ¶m.name.ident().as_str()[5..]), |
29 |
| - rustc_errors::Applicability::HasPlaceholders, |
30 |
| - rustc_errors::SuggestionStyle::ShowAlways, |
31 |
| - ); |
32 |
| - } else { |
33 |
| - diag.span_suggestion_with_style( |
34 |
| - Span::new( |
35 |
| - body.params[0].span.lo() - rustc_span::BytePos(1), |
36 |
| - ident.span.hi(), |
37 |
| - ident.span.ctxt(), |
38 |
| - ident.span.parent(), |
39 |
| - ), |
40 |
| - "add a type parameter", |
41 |
| - format!("<{{ /* Generic name */ }}: {}>", ¶m.name.ident().as_str()[5..]), |
42 |
| - rustc_errors::Applicability::HasPlaceholders, |
43 |
| - rustc_errors::SuggestionStyle::ShowAlways, |
44 |
| - ); |
45 |
| - } |
46 |
| - }, |
47 |
| - ); |
| 62 | + report(cx, param, ident, generics, body.params[0].span); |
| 63 | + }; |
| 64 | + } |
| 65 | + } |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { |
| 70 | + if_chain! { |
| 71 | + if let ImplItemKind::Fn(_, body_id) = impl_item.kind; |
| 72 | + if let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id()); |
| 73 | + if let hir::ItemKind::Impl(impl_) = item.kind; |
| 74 | + if let hir::Impl { of_trait, .. } = *impl_; |
| 75 | + if of_trait.is_none(); |
| 76 | + let body = cx.tcx.hir().body(body_id); |
| 77 | + if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public(); |
| 78 | + if !is_in_test_function(cx.tcx, impl_item.hir_id()); |
| 79 | + then { |
| 80 | + for param in impl_item.generics.params { |
| 81 | + if param.is_impl_trait() { |
| 82 | + report(cx, param, &impl_item.ident, impl_item.generics, body.params[0].span); |
| 83 | + } |
| 84 | + } |
| 85 | + } |
| 86 | + } |
| 87 | +} |
| 88 | + |
| 89 | +pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>, avoid_breaking_exported_api: bool) { |
| 90 | + if_chain! { |
| 91 | + if !avoid_breaking_exported_api; |
| 92 | + if let TraitItemKind::Fn(_, _) = trait_item.kind; |
| 93 | + if let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id()); |
| 94 | + // ^^ (Will always be a trait) |
| 95 | + if !item.vis_span.is_empty(); // Is public |
| 96 | + if !is_in_test_function(cx.tcx, trait_item.hir_id()); |
| 97 | + then { |
| 98 | + for param in trait_item.generics.params { |
| 99 | + if param.is_impl_trait() { |
| 100 | + let sp = trait_item.ident.span.with_hi(trait_item.ident.span.hi() + BytePos(1)); |
| 101 | + report(cx, param, &trait_item.ident, trait_item.generics, sp.shrink_to_hi()); |
48 | 102 | }
|
49 | 103 | }
|
50 | 104 | }
|
|
0 commit comments