mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-28 20:16:58 +03:00
135 lines
4.7 KiB
Rust
135 lines
4.7 KiB
Rust
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);
|
|
}
|
|
}
|