@@ -367,6 +367,23 @@ pub struct UnknownFormatParameterForOnUnimplementedAttr {
367
367
trait_name : Symbol ,
368
368
}
369
369
370
+ #[ derive( LintDiagnostic ) ]
371
+ #[ diag( trait_selection_disallowed_positional_argument) ]
372
+ #[ help]
373
+ pub struct DisallowedPositionalArgument ;
374
+
375
+ #[ derive( LintDiagnostic ) ]
376
+ #[ diag( trait_selection_invalid_format_specifier) ]
377
+ #[ help]
378
+ pub struct InvalidFormatSpecifier ;
379
+
380
+ #[ derive( LintDiagnostic ) ]
381
+ #[ diag( trait_selection_wrapped_parser_error) ]
382
+ pub struct WrappedParserError {
383
+ description : String ,
384
+ label : String ,
385
+ }
386
+
370
387
impl < ' tcx > OnUnimplementedDirective {
371
388
fn parse (
372
389
tcx : TyCtxt < ' tcx > ,
@@ -758,64 +775,108 @@ impl<'tcx> OnUnimplementedFormatString {
758
775
let trait_name = tcx. item_name ( trait_def_id) ;
759
776
let generics = tcx. generics_of ( item_def_id) ;
760
777
let s = self . symbol . as_str ( ) ;
761
- let parser = Parser :: new ( s, None , None , false , ParseMode :: Format ) ;
778
+ let mut parser = Parser :: new ( s, None , None , false , ParseMode :: Format ) ;
762
779
let mut result = Ok ( ( ) ) ;
763
- for token in parser {
780
+ for token in & mut parser {
764
781
match token {
765
782
Piece :: String ( _) => ( ) , // Normal string, no need to check it
766
- Piece :: NextArgument ( a) => match a. position {
767
- Position :: ArgumentNamed ( s) => {
768
- match Symbol :: intern ( s) {
769
- // `{ThisTraitsName}` is allowed
770
- s if s == trait_name && !self . is_diagnostic_namespace_variant => ( ) ,
771
- s if ALLOWED_FORMAT_SYMBOLS . contains ( & s)
772
- && !self . is_diagnostic_namespace_variant =>
773
- {
774
- ( )
775
- }
776
- // So is `{A}` if A is a type parameter
777
- s if generics. params . iter ( ) . any ( |param| param. name == s) => ( ) ,
778
- s => {
779
- if self . is_diagnostic_namespace_variant {
780
- tcx. emit_node_span_lint (
781
- UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES ,
782
- tcx. local_def_id_to_hir_id ( item_def_id. expect_local ( ) ) ,
783
- self . span ,
784
- UnknownFormatParameterForOnUnimplementedAttr {
785
- argument_name : s,
786
- trait_name,
787
- } ,
788
- ) ;
789
- } else {
790
- result = Err ( struct_span_code_err ! (
791
- tcx. dcx( ) ,
792
- self . span,
793
- E0230 ,
794
- "there is no parameter `{}` on {}" ,
795
- s,
796
- if trait_def_id == item_def_id {
797
- format!( "trait `{trait_name}`" )
798
- } else {
799
- "impl" . to_string( )
800
- }
801
- )
802
- . emit ( ) ) ;
783
+ Piece :: NextArgument ( a) => {
784
+ let format_spec = a. format ;
785
+ if self . is_diagnostic_namespace_variant
786
+ && ( format_spec. ty_span . is_some ( )
787
+ || format_spec. width_span . is_some ( )
788
+ || format_spec. precision_span . is_some ( )
789
+ || format_spec. fill_span . is_some ( ) )
790
+ {
791
+ tcx. emit_node_span_lint (
792
+ UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES ,
793
+ tcx. local_def_id_to_hir_id ( item_def_id. expect_local ( ) ) ,
794
+ self . span ,
795
+ InvalidFormatSpecifier ,
796
+ ) ;
797
+ }
798
+ match a. position {
799
+ Position :: ArgumentNamed ( s) => {
800
+ match Symbol :: intern ( s) {
801
+ // `{ThisTraitsName}` is allowed
802
+ s if s == trait_name && !self . is_diagnostic_namespace_variant => ( ) ,
803
+ s if ALLOWED_FORMAT_SYMBOLS . contains ( & s)
804
+ && !self . is_diagnostic_namespace_variant =>
805
+ {
806
+ ( )
807
+ }
808
+ // So is `{A}` if A is a type parameter
809
+ s if generics. params . iter ( ) . any ( |param| param. name == s) => ( ) ,
810
+ s => {
811
+ if self . is_diagnostic_namespace_variant {
812
+ tcx. emit_node_span_lint (
813
+ UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES ,
814
+ tcx. local_def_id_to_hir_id ( item_def_id. expect_local ( ) ) ,
815
+ self . span ,
816
+ UnknownFormatParameterForOnUnimplementedAttr {
817
+ argument_name : s,
818
+ trait_name,
819
+ } ,
820
+ ) ;
821
+ } else {
822
+ result = Err ( struct_span_code_err ! (
823
+ tcx. dcx( ) ,
824
+ self . span,
825
+ E0230 ,
826
+ "there is no parameter `{}` on {}" ,
827
+ s,
828
+ if trait_def_id == item_def_id {
829
+ format!( "trait `{trait_name}`" )
830
+ } else {
831
+ "impl" . to_string( )
832
+ }
833
+ )
834
+ . emit ( ) ) ;
835
+ }
803
836
}
804
837
}
805
838
}
839
+ // `{:1}` and `{}` are not to be used
840
+ Position :: ArgumentIs ( ..) | Position :: ArgumentImplicitlyIs ( _) => {
841
+ if self . is_diagnostic_namespace_variant {
842
+ tcx. emit_node_span_lint (
843
+ UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES ,
844
+ tcx. local_def_id_to_hir_id ( item_def_id. expect_local ( ) ) ,
845
+ self . span ,
846
+ DisallowedPositionalArgument ,
847
+ ) ;
848
+ } else {
849
+ let reported = struct_span_code_err ! (
850
+ tcx. dcx( ) ,
851
+ self . span,
852
+ E0231 ,
853
+ "only named generic parameters are allowed"
854
+ )
855
+ . emit ( ) ;
856
+ result = Err ( reported) ;
857
+ }
858
+ }
806
859
}
807
- // `{:1}` and `{}` are not to be used
808
- Position :: ArgumentIs ( ..) | Position :: ArgumentImplicitlyIs ( _) => {
809
- let reported = struct_span_code_err ! (
810
- tcx. dcx( ) ,
811
- self . span,
812
- E0231 ,
813
- "only named generic parameters are allowed"
814
- )
815
- . emit ( ) ;
816
- result = Err ( reported) ;
817
- }
818
- } ,
860
+ }
861
+ }
862
+ }
863
+ // we cannot return errors from processing the format string as hard error here
864
+ // as the diagnostic namespace gurantees that malformed input cannot cause an error
865
+ //
866
+ // if we encounter any error while processing we nevertheless want to show it as warning
867
+ // so that users are aware that something is not correct
868
+ for e in parser. errors {
869
+ if self . is_diagnostic_namespace_variant {
870
+ tcx. emit_node_span_lint (
871
+ UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES ,
872
+ tcx. local_def_id_to_hir_id ( item_def_id. expect_local ( ) ) ,
873
+ self . span ,
874
+ WrappedParserError { description : e. description , label : e. label } ,
875
+ ) ;
876
+ } else {
877
+ let reported =
878
+ struct_span_code_err ! ( tcx. dcx( ) , self . span, E0231 , "{}" , e. description, ) . emit ( ) ;
879
+ result = Err ( reported) ;
819
880
}
820
881
}
821
882
@@ -853,9 +914,9 @@ impl<'tcx> OnUnimplementedFormatString {
853
914
let empty_string = String :: new ( ) ;
854
915
855
916
let s = self . symbol . as_str ( ) ;
856
- let parser = Parser :: new ( s, None , None , false , ParseMode :: Format ) ;
917
+ let mut parser = Parser :: new ( s, None , None , false , ParseMode :: Format ) ;
857
918
let item_context = ( options. get ( & sym:: ItemContext ) ) . unwrap_or ( & empty_string) ;
858
- parser
919
+ let constructed_message = ( & mut parser)
859
920
. map ( |p| match p {
860
921
Piece :: String ( s) => s. to_owned ( ) ,
861
922
Piece :: NextArgument ( a) => match a. position {
@@ -895,9 +956,29 @@ impl<'tcx> OnUnimplementedFormatString {
895
956
}
896
957
}
897
958
}
959
+ Position :: ArgumentImplicitlyIs ( _) if self . is_diagnostic_namespace_variant => {
960
+ String :: from ( "{}" )
961
+ }
962
+ Position :: ArgumentIs ( idx) if self . is_diagnostic_namespace_variant => {
963
+ format ! ( "{{{idx}}}" )
964
+ }
898
965
_ => bug ! ( "broken on_unimplemented {:?} - bad format arg" , self . symbol) ,
899
966
} ,
900
967
} )
901
- . collect ( )
968
+ . collect ( ) ;
969
+ // we cannot return errors from processing the format string as hard error here
970
+ // as the diagnostic namespace gurantees that malformed input cannot cause an error
971
+ //
972
+ // if we encounter any error while processing the format string
973
+ // we don't want to show the potentially half assembled formated string,
974
+ // therefore we fall back to just showing the input string in this case
975
+ //
976
+ // The actual parser errors are emitted earlier
977
+ // as lint warnings in OnUnimplementedFormatString::verify
978
+ if self . is_diagnostic_namespace_variant && !parser. errors . is_empty ( ) {
979
+ String :: from ( s)
980
+ } else {
981
+ constructed_message
982
+ }
902
983
}
903
984
}
0 commit comments