@@ -39,7 +39,7 @@ use rustc_session::config::CrateType;
3939use rustc_session:: lint;
4040use rustc_session:: lint:: builtin:: {
4141 CONFLICTING_REPR_HINTS , INVALID_DOC_ATTRIBUTES , MALFORMED_DIAGNOSTIC_FORMAT_LITERALS ,
42- MISPLACED_DIAGNOSTIC_ATTRIBUTES , UNUSED_ATTRIBUTES ,
42+ MISPLACED_DIAGNOSTIC_ATTRIBUTES , ON_TYPE_ERROR_MULTIPLE_GENERICS , UNUSED_ATTRIBUTES ,
4343} ;
4444use rustc_session:: parse:: feature_err;
4545use rustc_span:: edition:: Edition ;
@@ -79,6 +79,10 @@ struct DiagnosticOnUnknownOnlyForImports {
7979 item_span : Span ,
8080}
8181
82+ #[ derive( Diagnostic ) ]
83+ #[ diag( "`#[diagnostic::on_type_error]` can only be applied to enums, structs or unions" ) ]
84+ struct DiagnosticOnTypeErrorOnlyForAdt ;
85+
8286fn target_from_impl_item < ' tcx > ( tcx : TyCtxt < ' tcx > , impl_item : & hir:: ImplItem < ' _ > ) -> Target {
8387 match impl_item. kind {
8488 hir:: ImplItemKind :: Const ( ..) => Target :: AssocConst ,
@@ -228,6 +232,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
228232 Attribute :: Parsed ( AttributeKind :: OnMove { span, directive } ) => {
229233 self . check_diagnostic_on_move ( * span, hir_id, target, directive. as_deref ( ) )
230234 } ,
235+ Attribute :: Parsed ( AttributeKind :: OnTypeError { span, directive } ) => {
236+ self . check_diagnostic_on_type_error ( * span, hir_id, target, directive. as_deref ( ) )
237+ } ,
231238 Attribute :: Parsed (
232239 // tidy-alphabetical-start
233240 AttributeKind :: RustcAllowIncoherentImpl ( ..)
@@ -688,6 +695,74 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
688695 }
689696 }
690697
698+ /// Checks if `#[diagnostic::on_type_error]` is applied to an ADT definition
699+ fn check_diagnostic_on_type_error (
700+ & self ,
701+ attr_span : Span ,
702+ hir_id : HirId ,
703+ target : Target ,
704+ directive : Option < & Directive > ,
705+ ) {
706+ if !matches ! ( target, Target :: Enum | Target :: Struct | Target :: Union ) {
707+ self . tcx . emit_node_span_lint (
708+ MISPLACED_DIAGNOSTIC_ATTRIBUTES ,
709+ hir_id,
710+ attr_span,
711+ DiagnosticOnTypeErrorOnlyForAdt ,
712+ ) ;
713+ }
714+
715+ if let Some ( directive) = directive {
716+ if let Node :: Item ( Item {
717+ kind :
718+ ItemKind :: Struct ( _, generics, _)
719+ | ItemKind :: Enum ( _, generics, _)
720+ | ItemKind :: Union ( _, generics, _) ,
721+ ..
722+ } ) = self . tcx . hir_node ( hir_id)
723+ {
724+ let generic_count = generics
725+ . params
726+ . iter ( )
727+ . filter ( |p| !matches ! ( p. kind, GenericParamKind :: Lifetime { .. } ) )
728+ . count ( ) ;
729+
730+ // Enforce: at most one generic
731+ if generic_count > 1 {
732+ self . tcx . emit_node_span_lint (
733+ ON_TYPE_ERROR_MULTIPLE_GENERICS ,
734+ hir_id,
735+ generics. span ,
736+ errors:: OnTypeErrorMultipleGenerics { count : generic_count } ,
737+ ) ;
738+ }
739+
740+ directive. visit_params ( & mut |argument_name, span| {
741+ let has_generic = generics. params . iter ( ) . any ( |p| {
742+ if !matches ! ( p. kind, GenericParamKind :: Lifetime { .. } )
743+ && let ParamName :: Plain ( name) = p. name
744+ && name. name == argument_name
745+ {
746+ true
747+ } else {
748+ false
749+ }
750+ } ) ;
751+
752+ let is_allowed = argument_name == sym:: Expected || argument_name == sym:: Found ;
753+ if !( has_generic | is_allowed) {
754+ self . tcx . emit_node_span_lint (
755+ MALFORMED_DIAGNOSTIC_FORMAT_LITERALS ,
756+ hir_id,
757+ span,
758+ errors:: OnTypeErrorMalformedFormatLiterals { name : argument_name } ,
759+ )
760+ }
761+ } ) ;
762+ }
763+ }
764+ }
765+
691766 /// Checks if an `#[inline]` is applied to a function or a closure.
692767 fn check_inline ( & self , hir_id : HirId , attr_span : Span , kind : & InlineAttr , target : Target ) {
693768 match target {
0 commit comments