@@ -15,6 +15,7 @@ use rustc_parse_format::{ParseMode, Parser, Piece, Position};
1515use rustc_session:: lint:: builtin:: UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES ;
1616use rustc_span:: symbol:: { kw, sym, Symbol } ;
1717use rustc_span:: { Span , DUMMY_SP } ;
18+ use std:: borrow:: Cow ;
1819use std:: iter;
1920
2021use crate :: errors:: {
@@ -321,7 +322,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
321322}
322323
323324#[ derive( Clone , Debug ) ]
324- pub struct OnUnimplementedFormatString ( Symbol , Span ) ;
325+ pub struct OnUnimplementedFormatString {
326+ symbol : Symbol ,
327+ span : Span ,
328+ is_diagnostic_namespace_variant : bool ,
329+ }
325330
326331#[ derive( Debug ) ]
327332pub struct OnUnimplementedDirective {
@@ -401,6 +406,14 @@ impl IgnoredDiagnosticOption {
401406 }
402407}
403408
409+ #[ derive( LintDiagnostic ) ]
410+ #[ diag( trait_selection_unknown_format_parameter_for_on_unimplemented_attr) ]
411+ #[ help]
412+ pub struct UnknownFormatParameterForOnUnimplementedAttr {
413+ argument_name : Symbol ,
414+ trait_name : Symbol ,
415+ }
416+
404417impl < ' tcx > OnUnimplementedDirective {
405418 fn parse (
406419 tcx : TyCtxt < ' tcx > ,
@@ -414,8 +427,14 @@ impl<'tcx> OnUnimplementedDirective {
414427 let mut item_iter = items. iter ( ) ;
415428
416429 let parse_value = |value_str, value_span| {
417- OnUnimplementedFormatString :: try_parse ( tcx, item_def_id, value_str, span, value_span)
418- . map ( Some )
430+ OnUnimplementedFormatString :: try_parse (
431+ tcx,
432+ item_def_id,
433+ value_str,
434+ value_span,
435+ is_diagnostic_namespace_variant,
436+ )
437+ . map ( Some )
419438 } ;
420439
421440 let condition = if is_root {
@@ -552,15 +571,15 @@ impl<'tcx> OnUnimplementedDirective {
552571 IgnoredDiagnosticOption :: maybe_emit_warning (
553572 tcx,
554573 item_def_id,
555- directive. message . as_ref ( ) . map ( |f| f. 1 ) ,
556- aggr. message . as_ref ( ) . map ( |f| f. 1 ) ,
574+ directive. message . as_ref ( ) . map ( |f| f. span ) ,
575+ aggr. message . as_ref ( ) . map ( |f| f. span ) ,
557576 "message" ,
558577 ) ;
559578 IgnoredDiagnosticOption :: maybe_emit_warning (
560579 tcx,
561580 item_def_id,
562- directive. label . as_ref ( ) . map ( |f| f. 1 ) ,
563- aggr. label . as_ref ( ) . map ( |f| f. 1 ) ,
581+ directive. label . as_ref ( ) . map ( |f| f. span ) ,
582+ aggr. label . as_ref ( ) . map ( |f| f. span ) ,
564583 "label" ,
565584 ) ;
566585 IgnoredDiagnosticOption :: maybe_emit_warning (
@@ -573,8 +592,8 @@ impl<'tcx> OnUnimplementedDirective {
573592 IgnoredDiagnosticOption :: maybe_emit_warning (
574593 tcx,
575594 item_def_id,
576- directive. parent_label . as_ref ( ) . map ( |f| f. 1 ) ,
577- aggr. parent_label . as_ref ( ) . map ( |f| f. 1 ) ,
595+ directive. parent_label . as_ref ( ) . map ( |f| f. span ) ,
596+ aggr. parent_label . as_ref ( ) . map ( |f| f. span ) ,
578597 "parent_label" ,
579598 ) ;
580599 IgnoredDiagnosticOption :: maybe_emit_warning (
@@ -634,7 +653,7 @@ impl<'tcx> OnUnimplementedDirective {
634653 item_def_id,
635654 value,
636655 attr. span ,
637- attr . span ,
656+ is_diagnostic_namespace_variant ,
638657 ) ?) ,
639658 notes : Vec :: new ( ) ,
640659 parent_label : None ,
@@ -712,7 +731,12 @@ impl<'tcx> OnUnimplementedDirective {
712731 // `with_no_visible_paths` is also used when generating the options,
713732 // so we need to match it here.
714733 ty:: print:: with_no_visible_paths!(
715- OnUnimplementedFormatString ( v, cfg. span) . format(
734+ OnUnimplementedFormatString {
735+ symbol: v,
736+ span: cfg. span,
737+ is_diagnostic_namespace_variant: false
738+ }
739+ . format(
716740 tcx,
717741 trait_ref,
718742 & options_map
@@ -760,20 +784,19 @@ impl<'tcx> OnUnimplementedFormatString {
760784 tcx : TyCtxt < ' tcx > ,
761785 item_def_id : DefId ,
762786 from : Symbol ,
763- err_sp : Span ,
764787 value_span : Span ,
788+ is_diagnostic_namespace_variant : bool ,
765789 ) -> Result < Self , ErrorGuaranteed > {
766- let result = OnUnimplementedFormatString ( from, value_span) ;
767- result. verify ( tcx, item_def_id, err_sp) ?;
790+ let result = OnUnimplementedFormatString {
791+ symbol : from,
792+ span : value_span,
793+ is_diagnostic_namespace_variant,
794+ } ;
795+ result. verify ( tcx, item_def_id) ?;
768796 Ok ( result)
769797 }
770798
771- fn verify (
772- & self ,
773- tcx : TyCtxt < ' tcx > ,
774- item_def_id : DefId ,
775- span : Span ,
776- ) -> Result < ( ) , ErrorGuaranteed > {
799+ fn verify ( & self , tcx : TyCtxt < ' tcx > , item_def_id : DefId ) -> Result < ( ) , ErrorGuaranteed > {
777800 let trait_def_id = if tcx. is_trait ( item_def_id) {
778801 item_def_id
779802 } else {
@@ -782,7 +805,7 @@ impl<'tcx> OnUnimplementedFormatString {
782805 } ;
783806 let trait_name = tcx. item_name ( trait_def_id) ;
784807 let generics = tcx. generics_of ( item_def_id) ;
785- let s = self . 0 . as_str ( ) ;
808+ let s = self . symbol . as_str ( ) ;
786809 let parser = Parser :: new ( s, None , None , false , ParseMode :: Format ) ;
787810 let mut result = Ok ( ( ) ) ;
788811 for token in parser {
@@ -792,32 +815,48 @@ impl<'tcx> OnUnimplementedFormatString {
792815 Position :: ArgumentNamed ( s) => {
793816 match Symbol :: intern ( s) {
794817 // `{ThisTraitsName}` is allowed
795- s if s == trait_name => ( ) ,
796- s if ALLOWED_FORMAT_SYMBOLS . contains ( & s) => ( ) ,
818+ s if s == trait_name && !self . is_diagnostic_namespace_variant => ( ) ,
819+ s if ALLOWED_FORMAT_SYMBOLS . contains ( & s)
820+ && !self . is_diagnostic_namespace_variant =>
821+ {
822+ ( )
823+ }
797824 // So is `{A}` if A is a type parameter
798825 s if generics. params . iter ( ) . any ( |param| param. name == s) => ( ) ,
799826 s => {
800- result = Err ( struct_span_err ! (
801- tcx. sess,
802- span,
803- E0230 ,
804- "there is no parameter `{}` on {}" ,
805- s,
806- if trait_def_id == item_def_id {
807- format!( "trait `{trait_name}`" )
808- } else {
809- "impl" . to_string( )
810- }
811- )
812- . emit ( ) ) ;
827+ if self . is_diagnostic_namespace_variant {
828+ tcx. emit_spanned_lint (
829+ UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES ,
830+ tcx. local_def_id_to_hir_id ( item_def_id. expect_local ( ) ) ,
831+ self . span ,
832+ UnknownFormatParameterForOnUnimplementedAttr {
833+ argument_name : s,
834+ trait_name,
835+ } ,
836+ ) ;
837+ } else {
838+ result = Err ( struct_span_err ! (
839+ tcx. sess,
840+ self . span,
841+ E0230 ,
842+ "there is no parameter `{}` on {}" ,
843+ s,
844+ if trait_def_id == item_def_id {
845+ format!( "trait `{trait_name}`" )
846+ } else {
847+ "impl" . to_string( )
848+ }
849+ )
850+ . emit ( ) ) ;
851+ }
813852 }
814853 }
815854 }
816855 // `{:1}` and `{}` are not to be used
817856 Position :: ArgumentIs ( ..) | Position :: ArgumentImplicitlyIs ( _) => {
818857 let reported = struct_span_err ! (
819858 tcx. sess,
820- span,
859+ self . span,
821860 E0231 ,
822861 "only named substitution parameters are allowed"
823862 )
@@ -856,45 +895,50 @@ impl<'tcx> OnUnimplementedFormatString {
856895 . collect :: < FxHashMap < Symbol , String > > ( ) ;
857896 let empty_string = String :: new ( ) ;
858897
859- let s = self . 0 . as_str ( ) ;
898+ let s = self . symbol . as_str ( ) ;
860899 let parser = Parser :: new ( s, None , None , false , ParseMode :: Format ) ;
861900 let item_context = ( options. get ( & sym:: ItemContext ) ) . unwrap_or ( & empty_string) ;
862901 parser
863902 . map ( |p| match p {
864- Piece :: String ( s) => s ,
903+ Piece :: String ( s) => Cow :: Borrowed ( s ) ,
865904 Piece :: NextArgument ( a) => match a. position {
866- Position :: ArgumentNamed ( s ) => {
867- let s = Symbol :: intern ( s ) ;
905+ Position :: ArgumentNamed ( arg ) => {
906+ let s = Symbol :: intern ( arg ) ;
868907 match generic_map. get ( & s) {
869- Some ( val) => val,
870- None if s == name => & trait_str,
908+ Some ( val) => Cow :: < str > :: Borrowed ( val) ,
909+ None if self . is_diagnostic_namespace_variant => {
910+ Cow :: Owned ( format ! ( "{{{arg}}}" ) )
911+ }
912+ None if s == name => Cow :: < str > :: Borrowed ( & trait_str) ,
871913 None => {
872914 if let Some ( val) = options. get ( & s) {
873- val
915+ Cow :: < str > :: Borrowed ( val)
874916 } else if s == sym:: from_desugaring {
875917 // don't break messages using these two arguments incorrectly
876- & empty_string
877- } else if s == sym:: ItemContext {
878- item_context
918+ Cow :: < str > :: Borrowed ( & empty_string as & str )
919+ } else if s == sym:: ItemContext
920+ && !self . is_diagnostic_namespace_variant
921+ {
922+ Cow :: < str > :: Borrowed ( item_context)
879923 } else if s == sym:: integral {
880- "{integral}"
924+ Cow :: < str > :: Borrowed ( "{integral}" )
881925 } else if s == sym:: integer_ {
882- "{integer}"
926+ Cow :: < str > :: Borrowed ( "{integer}" )
883927 } else if s == sym:: float {
884- "{float}"
928+ Cow :: < str > :: Borrowed ( "{float}" )
885929 } else {
886930 bug ! (
887931 "broken on_unimplemented {:?} for {:?}: \
888932 no argument matching {:?}",
889- self . 0 ,
933+ self . symbol ,
890934 trait_ref,
891935 s
892936 )
893937 }
894938 }
895939 }
896940 }
897- _ => bug ! ( "broken on_unimplemented {:?} - bad format arg" , self . 0 ) ,
941+ _ => bug ! ( "broken on_unimplemented {:?} - bad format arg" , self . symbol ) ,
898942 } ,
899943 } )
900944 . collect ( )
0 commit comments