blob: 6d5c7b86a0aed93c837f3fc80cb956ba92aed112 [file] [log] [blame] [edit]
use crate::internal_paths;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::sym;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::{self, GenericArgKind};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_tool_lint! {
/// ### What it does
/// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
pub clippy::MISSING_MSRV_ATTR_IMPL,
Warn,
"checking if all necessary steps were taken when adding a MSRV to a lint",
report_in_external_macro: true
}
declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]);
impl LateLintPass<'_> for MsrvAttrImpl {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
if let hir::ItemKind::Impl(hir::Impl {
of_trait: Some(_),
items,
..
}) = &item.kind
&& let trait_ref = cx.tcx.impl_trait_ref(item.owner_id).instantiate_identity()
&& internal_paths::EARLY_LINT_PASS.matches(cx, trait_ref.def_id)
&& let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind()
&& self_ty_def.is_struct()
&& self_ty_def.all_fields().any(|f| {
cx.tcx
.type_of(f.did)
.instantiate_identity()
.walk()
.filter(|t| matches!(t.kind(), GenericArgKind::Type(_)))
.any(|t| internal_paths::MSRV_STACK.matches_ty(cx, t.expect_ty()))
})
&& !items
.iter()
.any(|&item| cx.tcx.item_name(item.owner_id) == sym::check_attributes)
{
let span = cx.sess().source_map().span_through_char(item.span, '{');
span_lint_and_sugg(
cx,
MISSING_MSRV_ATTR_IMPL,
span,
"`extract_msrv_attr!` macro missing from `EarlyLintPass` implementation",
"add `extract_msrv_attr!()` to the `EarlyLintPass` implementation",
format!("{}\n extract_msrv_attr!();", snippet(cx, span, "..")),
Applicability::MachineApplicable,
);
}
}
}