@@ -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 ( ..)
@@ -687,6 +694,74 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
687694 }
688695 }
689696
697+ /// Checks if `#[diagnostic::on_type_error]` is applied to an ADT definition
698+ fn check_diagnostic_on_type_error (
699+ & self ,
700+ attr_span : Span ,
701+ hir_id : HirId ,
702+ target : Target ,
703+ directive : Option < & Directive > ,
704+ ) {
705+ if !matches ! ( target, Target :: Enum | Target :: Struct | Target :: Union ) {
706+ self . tcx . emit_node_span_lint (
707+ MISPLACED_DIAGNOSTIC_ATTRIBUTES ,
708+ hir_id,
709+ attr_span,
710+ DiagnosticOnTypeErrorOnlyForAdt ,
711+ ) ;
712+ }
713+
714+ if let Some ( directive) = directive {
715+ if let Node :: Item ( Item {
716+ kind :
717+ ItemKind :: Struct ( _, generics, _)
718+ | ItemKind :: Enum ( _, generics, _)
719+ | ItemKind :: Union ( _, generics, _) ,
720+ ..
721+ } ) = self . tcx . hir_node ( hir_id)
722+ {
723+ let generic_count = generics
724+ . params
725+ . iter ( )
726+ . filter ( |p| !matches ! ( p. kind, GenericParamKind :: Lifetime { .. } ) )
727+ . count ( ) ;
728+
729+ // Enforce: at most one generic
730+ if generic_count > 1 {
731+ self . tcx . emit_node_span_lint (
732+ ON_TYPE_ERROR_MULTIPLE_GENERICS ,
733+ hir_id,
734+ generics. span ,
735+ errors:: OnTypeErrorMultipleGenerics { count : generic_count } ,
736+ ) ;
737+ }
738+
739+ directive. visit_params ( & mut |argument_name, span| {
740+ let has_generic = generics. params . iter ( ) . any ( |p| {
741+ if !matches ! ( p. kind, GenericParamKind :: Lifetime { .. } )
742+ && let ParamName :: Plain ( name) = p. name
743+ && name. name == argument_name
744+ {
745+ true
746+ } else {
747+ false
748+ }
749+ } ) ;
750+
751+ let is_allowed = argument_name == sym:: Expected || argument_name == sym:: Found ;
752+ if !( has_generic | is_allowed) {
753+ self . tcx . emit_node_span_lint (
754+ MALFORMED_DIAGNOSTIC_FORMAT_LITERALS ,
755+ hir_id,
756+ span,
757+ errors:: OnTypeErrorMalformedFormatLiterals { name : argument_name } ,
758+ )
759+ }
760+ } ) ;
761+ }
762+ }
763+ }
764+
690765 /// Checks if an `#[inline]` is applied to a function or a closure.
691766 fn check_inline ( & self , hir_id : HirId , attr_span : Span , kind : & InlineAttr , target : Target ) {
692767 match target {
0 commit comments