|
| 1 | +use crate::functions::hir::GenericArg; |
| 2 | +use crate::functions::REF_OPTION_SIG; |
| 3 | +use clippy_utils::diagnostics::span_lint_and_then; |
| 4 | +use clippy_utils::source::snippet; |
| 5 | +use rustc_errors::Applicability; |
| 6 | +use rustc_hir as hir; |
| 7 | +use rustc_hir::{GenericArgs, ImplItem, MutTy, QPath, TraitItem, TyKind}; |
| 8 | +use rustc_lint::LateContext; |
| 9 | +use rustc_span::{sym, Span}; |
| 10 | + |
| 11 | +fn check_fn_sig(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, sig_span: Span) { |
| 12 | + let mut fixes = Vec::new(); |
| 13 | + for param in decl.inputs { |
| 14 | + if let TyKind::Ref(_, MutTy { ty, .. }) = param.kind |
| 15 | + // TODO: is this the right way to check if ty is an Option? |
| 16 | + && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind |
| 17 | + && path.segments.len() == 1 |
| 18 | + && let seg = &path.segments[0] |
| 19 | + && seg.ident.name == sym::Option |
| 20 | + // check if option contains a regular type, not a reference |
| 21 | + // TODO: Should this instead just check that opt_ty is a TyKind::Path? |
| 22 | + && let Some(GenericArgs { args: [GenericArg::Type(opt_ty)], .. }) = seg.args |
| 23 | + && !matches!(opt_ty.kind, TyKind::Ref(..)) |
| 24 | + { |
| 25 | + // FIXME: Should this use the Option path from the original type? |
| 26 | + // FIXME: Should reference be added in some other way to the snippet? |
| 27 | + fixes.push((param.span, format!("Option<&{}>", snippet(cx, opt_ty.span, "..")))); |
| 28 | + } |
| 29 | + } |
| 30 | + |
| 31 | + if !fixes.is_empty() { |
| 32 | + span_lint_and_then( |
| 33 | + cx, |
| 34 | + REF_OPTION_SIG, |
| 35 | + sig_span, |
| 36 | + "it is more idiomatic to use `Option<&T>` instead of `&Option<T>`", |
| 37 | + |diag| { |
| 38 | + diag.multipart_suggestion("change this to", fixes, Applicability::Unspecified); |
| 39 | + }, |
| 40 | + ); |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { |
| 45 | + if let hir::ItemKind::Fn(ref sig, _, _) = item.kind { |
| 46 | + check_fn_sig(cx, sig.decl, sig.span); |
| 47 | + } |
| 48 | +} |
| 49 | + |
| 50 | +pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { |
| 51 | + if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind { |
| 52 | + check_fn_sig(cx, sig.decl, sig.span); |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>) { |
| 57 | + if let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind { |
| 58 | + check_fn_sig(cx, sig.decl, sig.span); |
| 59 | + } |
| 60 | +} |
0 commit comments