| use crate::functions::REF_OPTION; | |
| use clippy_utils::diagnostics::span_lint_and_then; | |
| use clippy_utils::source::snippet; | |
| use clippy_utils::ty::option_arg_ty; | |
| use clippy_utils::{is_from_proc_macro, is_trait_impl_item}; | |
| use rustc_errors::Applicability; | |
| use rustc_hir::intravisit::FnKind; | |
| use rustc_hir::{self as hir, FnDecl, HirId}; | |
| use rustc_lint::{LateContext, LintContext}; | |
| use rustc_middle::ty::{self, Mutability, Ty}; | |
| use rustc_span::Span; | |
| use rustc_span::def_id::LocalDefId; | |
| fn check_ty<'a>(cx: &LateContext<'a>, param: &hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) { | |
| if !param.span.in_external_macro(cx.sess().source_map()) | |
| && let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind() | |
| && let Some(gen_ty) = option_arg_ty(cx, *opt_ty) | |
| && !gen_ty.is_ref() | |
| // Need to gen the original spans, so first parsing mid, and hir parsing afterward | |
| && let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind | |
| && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind | |
| && let (Some(first), Some(last)) = (path.segments.first(), path.segments.last()) | |
| && let Some(hir::GenericArgs { | |
| args: [hir::GenericArg::Type(opt_ty)], | |
| .. | |
| }) = last.args | |
| && !is_from_proc_macro(cx, param) | |
| { | |
| let lifetime = snippet(cx, lifetime.ident.span, ".."); | |
| fixes.push(( | |
| param.span, | |
| format!( | |
| "{}<&{lifetime}{}{}>", | |
| snippet(cx, first.ident.span.to(last.ident.span), ".."), | |
| if lifetime.is_empty() { "" } else { " " }, | |
| snippet(cx, opt_ty.span, "..") | |
| ), | |
| )); | |
| } | |
| } | |
| fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty::FnSig<'a>) { | |
| let mut fixes = Vec::new(); | |
| // Check function arguments' types | |
| for (param, param_ty) in decl.inputs.iter().zip(sig.inputs()) { | |
| check_ty(cx, param, *param_ty, &mut fixes); | |
| } | |
| // Check return type | |
| if let hir::FnRetTy::Return(ty) = &decl.output { | |
| check_ty(cx, ty, sig.output(), &mut fixes); | |
| } | |
| if !fixes.is_empty() { | |
| span_lint_and_then( | |
| cx, | |
| REF_OPTION, | |
| span, | |
| "it is more idiomatic to use `Option<&T>` instead of `&Option<T>`", | |
| |diag| { | |
| diag.multipart_suggestion("change this to", fixes, Applicability::Unspecified); | |
| }, | |
| ); | |
| } | |
| } | |
| #[expect(clippy::too_many_arguments)] | |
| pub(crate) fn check_fn<'a>( | |
| cx: &LateContext<'a>, | |
| kind: FnKind<'a>, | |
| decl: &FnDecl<'a>, | |
| span: Span, | |
| hir_id: HirId, | |
| def_id: LocalDefId, | |
| body: &hir::Body<'a>, | |
| avoid_breaking_exported_api: bool, | |
| ) { | |
| if avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) { | |
| return; | |
| } | |
| if span.in_external_macro(cx.sess().source_map()) { | |
| return; | |
| } | |
| if let FnKind::Closure = kind { | |
| // Compute the span of the closure parameters + return type if set | |
| let inputs_output_span = if let hir::FnRetTy::Return(out_ty) = &decl.output { | |
| if decl.inputs.is_empty() { | |
| out_ty.span | |
| } else { | |
| span.with_hi(out_ty.span.hi()) | |
| } | |
| } else if let (Some(first), Some(last)) = (decl.inputs.first(), decl.inputs.last()) { | |
| first.span.to(last.span) | |
| } else { | |
| // No parameters - no point in checking | |
| return; | |
| }; | |
| // Figure out the signature of the closure | |
| let ty::Closure(_, args) = cx.typeck_results().expr_ty(body.value).kind() else { | |
| return; | |
| }; | |
| let sig = args.as_closure().sig().skip_binder(); | |
| if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) { | |
| return; | |
| } | |
| check_fn_sig(cx, decl, inputs_output_span, sig); | |
| } else if !is_trait_impl_item(cx, hir_id) { | |
| let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); | |
| if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) { | |
| return; | |
| } | |
| check_fn_sig(cx, decl, span, sig); | |
| } | |
| } | |
| pub(super) fn check_trait_item<'a>( | |
| cx: &LateContext<'a>, | |
| trait_item: &hir::TraitItem<'a>, | |
| avoid_breaking_exported_api: bool, | |
| ) { | |
| if !trait_item.span.in_external_macro(cx.sess().source_map()) | |
| && let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind | |
| && !(avoid_breaking_exported_api && cx.effective_visibilities.is_exported(trait_item.owner_id.def_id)) | |
| && !is_from_proc_macro(cx, trait_item) | |
| { | |
| let def_id = trait_item.owner_id.def_id; | |
| let ty_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder(); | |
| check_fn_sig(cx, sig.decl, sig.span, ty_sig); | |
| } | |
| } |