@@ -23,12 +23,12 @@ use crate::common::{
23
23
output_base_dir, output_base_name, output_testname_unique,
24
24
} ;
25
25
use crate :: compute_diff:: { DiffLine , make_diff, write_diff, write_filtered_diff} ;
26
- use crate :: errors:: { Error , ErrorKind , load_errors} ;
26
+ use crate :: errors:: { Error , ErrorKind , ErrorMessage , load_errors} ;
27
27
use crate :: header:: TestProps ;
28
+ use crate :: json:: DiagnosticCode ;
28
29
use crate :: read2:: { Truncated , read2_abbreviated} ;
29
30
use crate :: util:: { Utf8PathBufExt , add_dylib_path, logv, static_regex} ;
30
31
use crate :: { ColorConfig , json, stamp_file_path} ;
31
-
32
32
mod debugger;
33
33
34
34
// Helper modules that implement test running logic for each test suite.
@@ -701,14 +701,17 @@ impl<'test> TestCx<'test> {
701
701
// Parse the JSON output from the compiler and extract out the messages.
702
702
let actual_errors = json:: parse_output ( & diagnostic_file_name, & self . get_output ( proc_res) )
703
703
. into_iter ( )
704
- . map ( |e| Error { msg : self . normalize_output ( & e. msg , & [ ] ) , ..e } ) ;
704
+ . map ( |mut e| {
705
+ e. msg . text = self . normalize_output ( & e. msg . text , & [ ] ) ;
706
+ e
707
+ } ) ;
705
708
706
709
let mut unexpected = Vec :: new ( ) ;
707
710
let mut found = vec ! [ false ; expected_errors. len( ) ] ;
708
711
for actual_error in actual_errors {
709
712
for pattern in & self . props . error_patterns {
710
713
let pattern = pattern. trim ( ) ;
711
- if actual_error. msg . contains ( pattern) {
714
+ if actual_error. msg . text . to_string ( ) . contains ( pattern) {
712
715
let q = if actual_error. line_num . is_none ( ) { "?" } else { "" } ;
713
716
self . fatal ( & format ! (
714
717
"error pattern '{pattern}' is found in structured \
@@ -723,7 +726,7 @@ impl<'test> TestCx<'test> {
723
726
!found[ index]
724
727
&& actual_error. line_num == expected_error. line_num
725
728
&& actual_error. kind == expected_error. kind
726
- && actual_error. msg . contains ( & expected_error. msg )
729
+ && actual_error. msg . to_string ( ) . contains ( & expected_error. msg . text )
727
730
} ) ;
728
731
729
732
match opt_index {
@@ -779,6 +782,10 @@ impl<'test> TestCx<'test> {
779
782
println ! ( "{}" , error. render_for_expected( ) ) ;
780
783
}
781
784
println ! ( "{}" , "---" . green( ) ) ;
785
+
786
+ if self . config . try_annotate {
787
+ self . try_annotate ( unexpected, file_name) ;
788
+ }
782
789
}
783
790
if !not_found. is_empty ( ) {
784
791
println ! ( "{}" , "--- not found errors (from test file) ---" . red( ) ) ;
@@ -2808,6 +2815,97 @@ impl<'test> TestCx<'test> {
2808
2815
println ! ( "init_incremental_test: incremental_dir={incremental_dir}" ) ;
2809
2816
}
2810
2817
}
2818
+
2819
+ fn try_annotate ( & self , unexpected : Vec < Error > , file_name : String ) {
2820
+ use std:: io:: { BufRead , Seek , Write } ;
2821
+
2822
+ let mut file = std:: fs:: OpenOptions :: new ( ) . write ( true ) . read ( true ) . open ( & file_name) . unwrap ( ) ;
2823
+ let br = BufReader :: new ( & file) ;
2824
+ let mut lines: Vec < _ > = br. lines ( ) . map ( |line| ( line. unwrap ( ) , Vec :: new ( ) ) ) . collect ( ) ;
2825
+ for error in & unexpected {
2826
+ let Some ( line_no) = error. line_num else { continue } ;
2827
+ let target_line = & mut lines[ line_no - 1 ] ;
2828
+ let text = error. msg . clone ( ) ;
2829
+ let annotation = ( error. kind , text) ;
2830
+ target_line. 1 . push ( annotation) ;
2831
+ }
2832
+
2833
+ file. set_len ( 0 ) . unwrap ( ) ;
2834
+ file. rewind ( ) . unwrap ( ) ;
2835
+
2836
+ let mut idx = 0 ;
2837
+ while let Some ( ( line, annots) ) = lines. get ( idx) {
2838
+ write ! ( file, "{line}" ) . unwrap ( ) ;
2839
+
2840
+ if let [ first, rest @ ..] = & * * annots {
2841
+ // where match ergonomics?
2842
+ let mut rest = rest;
2843
+
2844
+ // Reduce instability by trying to put the first annotation on the same line.
2845
+ // We care about this because error messages can mention the line number by,
2846
+ // for example, naming opaque types like `{[email protected] :11:22}`. If they
2847
+ // exist in a test then creating new lines before them invalidates those line numbers.
2848
+ if line. contains ( "//~" ) {
2849
+ // The line already has an annotation.
2850
+ rest = & * * annots
2851
+ } else {
2852
+ let ( kind, ErrorMessage { text, code, .. } ) = first;
2853
+
2854
+ if !( line. contains ( & kind. to_string ( ) ) && line. contains ( & * text) ) {
2855
+ // In the case of revisions, where a test is ran multiple times,
2856
+ // we do not want to add the same annotation multiple times!
2857
+ write ! ( file, " //~{kind} {text}" ) . unwrap ( ) ;
2858
+ if let Some ( DiagnosticCode { code } ) = code {
2859
+ write ! ( file, " [{code}]" ) . unwrap ( ) ;
2860
+ }
2861
+ }
2862
+ writeln ! ( file) . unwrap ( ) ;
2863
+ }
2864
+
2865
+ // Is the next line an error annotation? If so then
2866
+ // we cannot push the annotation there because that will
2867
+ // displace the one that is already there.
2868
+ //
2869
+ // Forward until we're clear of existing annotations.
2870
+ let mut carets = 1 ;
2871
+ while let Some ( ( maybe_annot, expect_empty) ) = lines. get ( idx + 1 ) {
2872
+ if maybe_annot. trim ( ) . starts_with ( "//~" ) {
2873
+ assert ! ( expect_empty. is_empty( ) , "did not expect an annotation" ) ;
2874
+ writeln ! ( file, "{maybe_annot}" ) . unwrap ( ) ;
2875
+ carets += 1 ;
2876
+ idx += 1 ;
2877
+ } else {
2878
+ break ;
2879
+ }
2880
+ }
2881
+
2882
+ // What is the previous line's offset?
2883
+ // Let's give the next annotation that offset.
2884
+ let offset = line. split_terminator ( |c : char | !c. is_whitespace ( ) ) . next ( ) ;
2885
+
2886
+ for ( kind, ErrorMessage { text, code, .. } ) in rest {
2887
+ // In the case of revisions, where a test is ran multiple times,
2888
+ // we do not want to add the same annotation multiple times!
2889
+ if !( line. contains ( & kind. to_string ( ) ) && line. contains ( & * text) ) {
2890
+ if let Some ( w) = offset {
2891
+ write ! ( file, "{w}" ) . unwrap ( ) ;
2892
+ }
2893
+ write ! ( file, "//~{:^>carets$}{kind} {text}" , "" ) . unwrap ( ) ;
2894
+ if let Some ( DiagnosticCode { code } ) = code {
2895
+ write ! ( file, " [{code}]" ) . unwrap ( ) ;
2896
+ }
2897
+ }
2898
+ writeln ! ( file) . unwrap ( ) ;
2899
+ carets += 1 ;
2900
+ }
2901
+ } else {
2902
+ writeln ! ( file) . unwrap ( ) ;
2903
+ }
2904
+
2905
+ idx += 1
2906
+ }
2907
+ file. flush ( ) . unwrap ( ) ;
2908
+ }
2811
2909
}
2812
2910
2813
2911
struct ProcArgs {
0 commit comments