@@ -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( ) ) ;
@@ -2801,6 +2808,97 @@ impl<'test> TestCx<'test> {
2801
2808
println ! ( "init_incremental_test: incremental_dir={incremental_dir}" ) ;
2802
2809
}
2803
2810
}
2811
+
2812
+ fn try_annotate ( & self , unexpected : Vec < Error > , file_name : String ) {
2813
+ use std:: io:: { BufRead , Seek , Write } ;
2814
+
2815
+ let mut file = std:: fs:: OpenOptions :: new ( ) . write ( true ) . read ( true ) . open ( & file_name) . unwrap ( ) ;
2816
+ let br = BufReader :: new ( & file) ;
2817
+ let mut lines: Vec < _ > = br. lines ( ) . map ( |line| ( line. unwrap ( ) , Vec :: new ( ) ) ) . collect ( ) ;
2818
+ for error in & unexpected {
2819
+ let Some ( line_no) = error. line_num else { continue } ;
2820
+ let target_line = & mut lines[ line_no - 1 ] ;
2821
+ let text = error. msg . clone ( ) ;
2822
+ let annotation = ( error. kind , text) ;
2823
+ target_line. 1 . push ( annotation) ;
2824
+ }
2825
+
2826
+ file. set_len ( 0 ) . unwrap ( ) ;
2827
+ file. rewind ( ) . unwrap ( ) ;
2828
+
2829
+ let mut idx = 0 ;
2830
+ while let Some ( ( line, annots) ) = lines. get ( idx) {
2831
+ write ! ( file, "{line}" ) . unwrap ( ) ;
2832
+
2833
+ if let [ first, rest @ ..] = & * * annots {
2834
+ // where match ergonomics?
2835
+ let mut rest = rest;
2836
+
2837
+ // Reduce instability by trying to put the first annotation on the same line.
2838
+ // We care about this because error messages can mention the line number by,
2839
+ // for example, naming opaque types like `{[email protected] :11:22}`. If they
2840
+ // exist in a test then creating new lines before them invalidates those line numbers.
2841
+ if line. contains ( "//~" ) {
2842
+ // The line already has an annotation.
2843
+ rest = & * * annots
2844
+ } else {
2845
+ let ( kind, ErrorMessage { text, code, .. } ) = first;
2846
+
2847
+ if !( line. contains ( & kind. to_string ( ) ) && line. contains ( & * text) ) {
2848
+ // In the case of revisions, where a test is ran multiple times,
2849
+ // we do not want to add the same annotation multiple times!
2850
+ write ! ( file, " //~{kind} {text}" ) . unwrap ( ) ;
2851
+ if let Some ( DiagnosticCode { code } ) = code {
2852
+ write ! ( file, " [{code}]" ) . unwrap ( ) ;
2853
+ }
2854
+ }
2855
+ writeln ! ( file) . unwrap ( ) ;
2856
+ }
2857
+
2858
+ // Is the next line an error annotation? If so then
2859
+ // we cannot push the annotation there because that will
2860
+ // displace the one that is already there.
2861
+ //
2862
+ // Forward until we're clear of existing annotations.
2863
+ let mut carets = 1 ;
2864
+ while let Some ( ( maybe_annot, expect_empty) ) = lines. get ( idx + 1 ) {
2865
+ if maybe_annot. trim ( ) . starts_with ( "//~" ) {
2866
+ assert ! ( expect_empty. is_empty( ) , "did not expect an annotation" ) ;
2867
+ writeln ! ( file, "{maybe_annot}" ) . unwrap ( ) ;
2868
+ carets += 1 ;
2869
+ idx += 1 ;
2870
+ } else {
2871
+ break ;
2872
+ }
2873
+ }
2874
+
2875
+ // What is the previous line's offset?
2876
+ // Let's give the next annotation that offset.
2877
+ let offset = line. split_terminator ( |c : char | !c. is_whitespace ( ) ) . next ( ) ;
2878
+
2879
+ for ( kind, ErrorMessage { text, code, .. } ) in rest {
2880
+ // In the case of revisions, where a test is ran multiple times,
2881
+ // we do not want to add the same annotation multiple times!
2882
+ if !( line. contains ( & kind. to_string ( ) ) && line. contains ( & * text) ) {
2883
+ if let Some ( w) = offset {
2884
+ write ! ( file, "{w}" ) . unwrap ( ) ;
2885
+ }
2886
+ write ! ( file, "//~{:^>carets$}{kind} {text}" , "" ) . unwrap ( ) ;
2887
+ if let Some ( DiagnosticCode { code } ) = code {
2888
+ write ! ( file, " [{code}]" ) . unwrap ( ) ;
2889
+ }
2890
+ }
2891
+ writeln ! ( file) . unwrap ( ) ;
2892
+ carets += 1 ;
2893
+ }
2894
+ } else {
2895
+ writeln ! ( file) . unwrap ( ) ;
2896
+ }
2897
+
2898
+ idx += 1
2899
+ }
2900
+ file. flush ( ) . unwrap ( ) ;
2901
+ }
2804
2902
}
2805
2903
2806
2904
struct ProcArgs {
0 commit comments