@@ -23,12 +23,12 @@ use crate::common::{
2323 output_base_dir, output_base_name, output_testname_unique,
2424} ;
2525use 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} ;
2727use crate :: header:: TestProps ;
28+ use crate :: json:: DiagnosticCode ;
2829use crate :: read2:: { Truncated , read2_abbreviated} ;
2930use crate :: util:: { Utf8PathBufExt , add_dylib_path, logv, static_regex} ;
3031use crate :: { ColorConfig , json, stamp_file_path} ;
31-
3232mod debugger;
3333
3434// Helper modules that implement test running logic for each test suite.
@@ -701,14 +701,17 @@ impl<'test> TestCx<'test> {
701701 // Parse the JSON output from the compiler and extract out the messages.
702702 let actual_errors = json:: parse_output ( & diagnostic_file_name, & self . get_output ( proc_res) )
703703 . 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+ } ) ;
705708
706709 let mut unexpected = Vec :: new ( ) ;
707710 let mut found = vec ! [ false ; expected_errors. len( ) ] ;
708711 for actual_error in actual_errors {
709712 for pattern in & self . props . error_patterns {
710713 let pattern = pattern. trim ( ) ;
711- if actual_error. msg . contains ( pattern) {
714+ if actual_error. msg . text . to_string ( ) . contains ( pattern) {
712715 let q = if actual_error. line_num . is_none ( ) { "?" } else { "" } ;
713716 self . fatal ( & format ! (
714717 "error pattern '{pattern}' is found in structured \
@@ -723,7 +726,7 @@ impl<'test> TestCx<'test> {
723726 !found[ index]
724727 && actual_error. line_num == expected_error. line_num
725728 && 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 )
727730 } ) ;
728731
729732 match opt_index {
@@ -779,6 +782,10 @@ impl<'test> TestCx<'test> {
779782 println ! ( "{}" , error. render_for_expected( ) ) ;
780783 }
781784 println ! ( "{}" , "---" . green( ) ) ;
785+
786+ if self . config . try_annotate {
787+ self . try_annotate ( unexpected, file_name) ;
788+ }
782789 }
783790 if !not_found. is_empty ( ) {
784791 println ! ( "{}" , "--- not found errors (from test file) ---" . red( ) ) ;
@@ -2808,6 +2815,97 @@ impl<'test> TestCx<'test> {
28082815 println ! ( "init_incremental_test: incremental_dir={incremental_dir}" ) ;
28092816 }
28102817 }
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 `{closure@file.rs: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+ }
28112909}
28122910
28132911struct ProcArgs {
0 commit comments