11use std:: ops:: Range ;
22
3- use rustc_errors:: E0232 ;
3+ use rustc_errors:: { Diagnostic , E0232 } ;
44use rustc_hir:: AttrPath ;
55use rustc_hir:: attrs:: diagnostic:: {
66 Directive , FilterFormatString , Flag , FormatArg , FormatString , LitOrArg , Name , NameValue ,
77 OnUnimplementedCondition , Piece , Predicate ,
88} ;
9- use rustc_hir:: lints:: { AttributeLintKind , FormatWarning } ;
9+ use rustc_hir:: lints:: AttributeLintKind ;
1010use rustc_macros:: Diagnostic ;
1111use rustc_parse_format:: {
1212 Argument , FormatSpec , ParseError , ParseMode , Parser , Piece as RpfPiece , Position ,
@@ -18,6 +18,7 @@ use rustc_span::{Ident, InnerSpan, Span, Symbol, kw, sym};
1818use thin_vec:: { ThinVec , thin_vec} ;
1919
2020use crate :: context:: { AcceptContext , Stage } ;
21+ use crate :: errors:: FormatWarning ;
2122use crate :: parser:: { ArgParser , MetaItemListParser , MetaItemOrLitParser , MetaItemParser } ;
2223
2324pub ( crate ) mod do_not_recommend;
@@ -83,6 +84,26 @@ impl Mode {
8384 Self :: DiagnosticOnUnmatchArgs => DEFAULT ,
8485 }
8586 }
87+
88+ fn allowed_format_arguments ( & self ) -> & ' static str {
89+ match self {
90+ Self :: RustcOnUnimplemented => {
91+ "see <https://rustc-dev-guide.rust-lang.org/diagnostics.html#rustc_on_unimplemented> for allowed format arguments"
92+ }
93+ Self :: DiagnosticOnUnimplemented => {
94+ "only `Self` and generics of the trait are allowed as a format argument"
95+ }
96+ Self :: DiagnosticOnConst => {
97+ "only `Self` and generics of the implementation are allowed as a format argument"
98+ }
99+ Self :: DiagnosticOnMove => {
100+ "only `This`, `Self` and generics of the type are allowed as a format argument"
101+ }
102+ Self :: DiagnosticOnUnknown | Self :: DiagnosticOnUnmatchArgs => {
103+ "only `This` is allowed as a format argument"
104+ }
105+ }
106+ }
86107}
87108
88109fn merge_directives < S : Stage > (
@@ -242,12 +263,13 @@ fn parse_directive_items<'p, S: Stage>(
242263 match parse_format_string ( input. name , snippet, input. span , mode) {
243264 Ok ( ( f, warnings) ) => {
244265 for warning in warnings {
245- let ( FormatWarning :: InvalidSpecifier { span, .. }
246- | FormatWarning :: PositionalArgument { span, .. }
247- | FormatWarning :: DisallowedPlaceholder { span } ) = warning;
248- cx. emit_lint (
266+ let ( FormatWarning :: InvalidSpecifier { span }
267+ | FormatWarning :: PositionalArgument { span }
268+ | FormatWarning :: IndexedArgument { span }
269+ | FormatWarning :: DisallowedPlaceholder { span, .. } ) = warning;
270+ cx. emit_dyn_lint (
249271 MALFORMED_DIAGNOSTIC_FORMAT_LITERALS ,
250- AttributeLintKind :: MalformedDiagnosticFormat { warning } ,
272+ move |dcx , level| warning. into_diag ( dcx , level ) ,
251273 span,
252274 ) ;
253275 }
@@ -394,38 +416,74 @@ fn parse_arg(
394416 is_source_literal: bool ,
395417) -> FormatArg {
396418 let span = slice_span ( input_span, arg. position_span . clone ( ) , is_source_literal) ;
397- if matches!( mode, Mode :: DiagnosticOnUnknown ) {
398- warnings. push ( FormatWarning :: DisallowedPlaceholder { span } ) ;
399- return FormatArg :: AsIs ( sym:: empty_braces) ;
400- }
401419
402420 match arg. position {
403421 // Something like "hello {name}"
404422 Position :: ArgumentNamed ( name) => match ( mode, Symbol :: intern ( name) ) {
405- // Only `#[rustc_on_unimplemented]` can use these
406- ( Mode :: RustcOnUnimplemented { .. } , sym:: ItemContext ) => FormatArg :: ItemContext ,
407- ( Mode :: RustcOnUnimplemented { .. } | Mode :: DiagnosticOnUnmatchArgs , sym:: This ) => {
408- FormatArg :: This
423+ ( Mode :: RustcOnUnimplemented , sym:: ItemContext ) => FormatArg :: ItemContext ,
424+
425+ // Like `{This}`, but sugared.
426+ // FIXME(mejrs) maybe rename/rework this or something
427+ // if we want to apply this to other attrs?
428+ ( Mode :: RustcOnUnimplemented , sym:: Trait ) => FormatArg :: Trait ,
429+
430+ // Some diagnostic attributes can use `{This}` to refer to the annotated item.
431+ // For those that don't, we continue and maybe use it as a generic parameter.
432+ //
433+ // FIXME(mejrs) `DiagnosticOnUnimplemented` is intentionally not here;
434+ // that requires lang approval which is best kept for a standalone PR.
435+ (
436+ Mode :: RustcOnUnimplemented
437+ | Mode :: DiagnosticOnUnknown
438+ | Mode :: DiagnosticOnMove
439+ | Mode :: DiagnosticOnUnmatchArgs ,
440+ sym:: This ,
441+ ) => FormatArg :: This ,
442+
443+ // `{Self}`; the self type.
444+ // - For trait declaration attributes that's the type that does not implement it.
445+ // - for trait impl attributes, the implemented for type.
446+ // - For ADT attributes, that's the type (which will be identical to `{This}`)
447+ // - For everything else it doesn't make sense.
448+ (
449+ Mode :: RustcOnUnimplemented
450+ | Mode :: DiagnosticOnUnimplemented
451+ | Mode :: DiagnosticOnMove
452+ | Mode :: DiagnosticOnConst ,
453+ kw:: SelfUpper ,
454+ ) => FormatArg :: SelfUpper ,
455+
456+ // Generic parameters.
457+ // FIXME(mejrs) unfortunately, all the "special" symbols above might fall through,
458+ // but at this time we are not aware of what generic parameters the trait actually has.
459+ // If we find `ItemContext` or something we have to assume that's a generic parameter.
460+ // We lint against that in `check_attr.rs` though.
461+ (
462+ Mode :: RustcOnUnimplemented
463+ | Mode :: DiagnosticOnUnimplemented
464+ | Mode :: DiagnosticOnMove
465+ | Mode :: DiagnosticOnConst ,
466+ generic_param,
467+ ) => FormatArg :: GenericParam { generic_param, span } ,
468+
469+ // Generics are explicitly not allowed, we print those back as is.
470+ ( Mode :: DiagnosticOnUnknown | Mode :: DiagnosticOnUnmatchArgs , as_is) => {
471+ warnings. push ( FormatWarning :: DisallowedPlaceholder {
472+ span,
473+ attr : mode. as_str ( ) ,
474+ allowed : mode. allowed_format_arguments ( ) ,
475+ } ) ;
476+ return FormatArg :: AsIs ( Symbol :: intern ( & format ! ( "{{{as_is}}}" ) ) ) ;
409477 }
410- ( Mode :: RustcOnUnimplemented { .. } , sym:: Trait ) => FormatArg :: Trait ,
411- // Any attribute can use these
412- ( _, kw:: SelfUpper ) => FormatArg :: SelfUpper ,
413- ( _, generic_param) => FormatArg :: GenericParam { generic_param, span } ,
414478 } ,
415479
416480 // `{:1}` and `{}` are ignored
417481 Position :: ArgumentIs ( idx) => {
418- warnings. push ( FormatWarning :: PositionalArgument {
419- span,
420- help : format ! ( "use `{{{idx}}}` to print a number in braces" ) ,
421- } ) ;
482+ warnings. push ( FormatWarning :: IndexedArgument { span } ) ;
422483 FormatArg :: AsIs ( Symbol :: intern ( & format ! ( "{{{idx}}}" ) ) )
423484 }
424485 Position :: ArgumentImplicitlyIs ( _) => {
425- warnings. push ( FormatWarning :: PositionalArgument {
426- span,
427- help : String :: from ( "use `{{}}` to print empty braces" ) ,
428- } ) ;
486+ warnings. push ( FormatWarning :: PositionalArgument { span } ) ;
429487 FormatArg :: AsIs ( sym:: empty_braces)
430488 }
431489 }
@@ -445,7 +503,7 @@ fn warn_on_format_spec(
445503 . as_ref ( )
446504 . map ( |inner| slice_span ( input_span, inner. clone ( ) , is_source_literal) )
447505 . unwrap_or ( input_span) ;
448- warnings. push ( FormatWarning :: InvalidSpecifier { span, name : spec . ty . into ( ) } )
506+ warnings. push ( FormatWarning :: InvalidSpecifier { span } )
449507 }
450508}
451509
0 commit comments