@@ -25,7 +25,7 @@ use rustc_hir::{
2525use rustc_lint:: { EarlyContext , EarlyLintPass , LateContext , LateLintPass , LintContext } ;
2626use rustc_middle:: hir:: nested_filter;
2727use rustc_middle:: mir:: interpret:: ConstValue ;
28- use rustc_middle:: ty;
28+ use rustc_middle:: ty:: { self , subst :: GenericArgKind } ;
2929use rustc_semver:: RustcVersion ;
3030use rustc_session:: { declare_lint_pass, declare_tool_lint, impl_lint_pass} ;
3131use rustc_span:: source_map:: Spanned ;
@@ -337,6 +337,15 @@ declare_clippy_lint! {
337337 "found clippy lint without `clippy::version` attribute"
338338}
339339
340+ declare_clippy_lint ! {
341+ /// ### What it does
342+ /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
343+ ///
344+ pub MISSING_MSRV_ATTR_IMPL ,
345+ internal,
346+ "checking if all necessary steps were taken when adding a MSRV to a lint"
347+ }
348+
340349declare_lint_pass ! ( ClippyLintsInternal => [ CLIPPY_LINTS_INTERNAL ] ) ;
341350
342351impl EarlyLintPass for ClippyLintsInternal {
@@ -1314,3 +1323,46 @@ fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: S
13141323 span. parent ( ) ,
13151324 )
13161325}
1326+
1327+ declare_lint_pass ! ( MsrvAttrImpl => [ MISSING_MSRV_ATTR_IMPL ] ) ;
1328+
1329+ impl LateLintPass < ' _ > for MsrvAttrImpl {
1330+ fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & hir:: Item < ' _ > ) {
1331+ if_chain ! {
1332+ if let hir:: ItemKind :: Impl ( hir:: Impl {
1333+ of_trait: Some ( lint_pass_trait_ref) ,
1334+ self_ty,
1335+ items,
1336+ ..
1337+ } ) = & item. kind;
1338+ if let Some ( lint_pass_trait_def_id) = lint_pass_trait_ref. trait_def_id( ) ;
1339+ let is_late_pass = match_def_path( cx, lint_pass_trait_def_id, & paths:: LATE_LINT_PASS ) ;
1340+ if is_late_pass || match_def_path( cx, lint_pass_trait_def_id, & paths:: EARLY_LINT_PASS ) ;
1341+ let self_ty = hir_ty_to_ty( cx. tcx, self_ty) ;
1342+ if let ty:: Adt ( self_ty_def, _) = self_ty. kind( ) ;
1343+ if self_ty_def. is_struct( ) ;
1344+ if self_ty_def. all_fields( ) . any( |f| {
1345+ cx. tcx
1346+ . type_of( f. did)
1347+ . walk( )
1348+ . filter( |t| matches!( t. unpack( ) , GenericArgKind :: Type ( _) ) )
1349+ . any( |t| match_type( cx, t. expect_ty( ) , & paths:: RUSTC_VERSION ) )
1350+ } ) ;
1351+ if !items. iter( ) . any( |item| item. ident. name == sym!( enter_lint_attrs) ) ;
1352+ then {
1353+ let context = if is_late_pass { "LateContext" } else { "EarlyContext" } ;
1354+ let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" } ;
1355+ let span = cx. sess( ) . source_map( ) . span_through_char( item. span, '{' ) ;
1356+ span_lint_and_sugg(
1357+ cx,
1358+ MISSING_MSRV_ATTR_IMPL ,
1359+ span,
1360+ & format!( "`extract_msrv_attr!` macro missing from `{lint_pass}` implementation" ) ,
1361+ & format!( "add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation" ) ,
1362+ format!( "{}\n extract_msrv_attr!({context});" , snippet( cx, span, ".." ) ) ,
1363+ Applicability :: MachineApplicable ,
1364+ ) ;
1365+ }
1366+ }
1367+ }
1368+ }
0 commit comments