@@ -559,7 +559,9 @@ pub(crate) struct LinkOrdinalOutOfRange {
559559}
560560
561561pub ( crate ) enum AttributeParseErrorReason < ' a > {
562- ExpectedNoArgs ,
562+ ExpectedNoArgs {
563+ path : & ' a AttrPath ,
564+ } ,
563565 ExpectedStringLiteral {
564566 byte_string : Option < Span > ,
565567 } ,
@@ -577,6 +579,9 @@ pub(crate) enum AttributeParseErrorReason<'a> {
577579 list : bool ,
578580 } ,
579581 ExpectedIdentifier ,
582+ ExpectedEnd {
583+ last : Span ,
584+ } ,
580585}
581586
582587pub ( crate ) struct AttributeParseError < ' a > {
@@ -588,13 +593,28 @@ pub(crate) struct AttributeParseError<'a> {
588593 pub ( crate ) reason : AttributeParseErrorReason < ' a > ,
589594}
590595
596+ /// based on the attribute's template we add relevant suggestions to the error automatically.
597+ enum DefaultSuggestionStyle {
598+ /// give a hint about the valid forms of the attribute.
599+ /// Useful if there's already a better suggestion given than the automatic ones can provide
600+ /// but we'd still like to show which syntax forms are valid.
601+ Hint ,
602+ /// Use the template to suggest changes to the attribute
603+ Suggestion ,
604+ /// Don't show any default suggestions
605+ None ,
606+ }
607+
591608impl < ' a , G : EmissionGuarantee > Diagnostic < ' a , G > for AttributeParseError < ' _ > {
592609 fn into_diag ( self , dcx : DiagCtxtHandle < ' a > , level : Level ) -> Diag < ' a , G > {
593610 let name = self . attribute . to_string ( ) ;
594611
595612 let mut diag = Diag :: new ( dcx, level, format ! ( "malformed `{name}` attribute input" ) ) ;
596613 diag. span ( self . attr_span ) ;
597614 diag. code ( E0539 ) ;
615+
616+ let mut show_default_suggestions = DefaultSuggestionStyle :: Suggestion ;
617+
598618 match self . reason {
599619 AttributeParseErrorReason :: ExpectedStringLiteral { byte_string } => {
600620 if let Some ( start_point_span) = byte_string {
@@ -606,7 +626,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
606626 ) ;
607627 diag. note ( "expected a normal string literal, not a byte string literal" ) ;
608628
609- return diag ;
629+ show_default_suggestions = DefaultSuggestionStyle :: None ;
610630 } else {
611631 diag. span_label ( self . span , "expected a string literal here" ) ;
612632 }
@@ -632,9 +652,19 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
632652 diag. span_label ( self . span , "didn't expect a literal here" ) ;
633653 diag. code ( E0565 ) ;
634654 }
635- AttributeParseErrorReason :: ExpectedNoArgs => {
655+ AttributeParseErrorReason :: ExpectedNoArgs { path } => {
636656 diag. span_label ( self . span , "didn't expect any arguments here" ) ;
637657 diag. code ( E0565 ) ;
658+
659+ if path. span != self . attribute . span {
660+ diag. span_suggestion (
661+ path. span . to ( self . span ) ,
662+ "remove this argument" ,
663+ path,
664+ Applicability :: MachineApplicable ,
665+ ) ;
666+ show_default_suggestions = DefaultSuggestionStyle :: Hint ;
667+ }
638668 }
639669 AttributeParseErrorReason :: ExpectedNameValue ( None ) => {
640670 // If the span is the entire attribute, the suggestion we add below this match already contains enough information
@@ -716,23 +746,36 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
716746 AttributeParseErrorReason :: ExpectedIdentifier => {
717747 diag. span_label ( self . span , "expected a valid identifier here" ) ;
718748 }
749+ AttributeParseErrorReason :: ExpectedEnd { last } => {
750+ diag. span_label ( last, "expected no more arguments after this" ) ;
751+ diag. span_label ( self . span , "remove this argument" ) ;
752+ }
719753 }
720754
721755 if let Some ( link) = self . template . docs {
722756 diag. note ( format ! ( "for more information, visit <{link}>" ) ) ;
723757 }
758+
724759 let suggestions = self . template . suggestions ( self . attr_style , & name) ;
760+ let text = match show_default_suggestions {
761+ DefaultSuggestionStyle :: Hint => {
762+ if suggestions. len ( ) == 1 {
763+ "the only valid form of the attribute is"
764+ } else {
765+ "these are the valid forms of the attribute"
766+ }
767+ }
768+ DefaultSuggestionStyle :: Suggestion => {
769+ if suggestions. len ( ) == 1 {
770+ "must be of the form"
771+ } else {
772+ "try changing it to one of the following valid forms of the attribute"
773+ }
774+ }
775+ DefaultSuggestionStyle :: None => return diag,
776+ } ;
725777
726- diag. span_suggestions (
727- self . attr_span ,
728- if suggestions. len ( ) == 1 {
729- "must be of the form"
730- } else {
731- "try changing it to one of the following valid forms of the attribute"
732- } ,
733- suggestions,
734- Applicability :: HasPlaceholders ,
735- ) ;
778+ diag. span_suggestions ( self . attr_span , text, suggestions, Applicability :: HasPlaceholders ) ;
736779
737780 diag
738781 }
0 commit comments