@@ -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( ) ) ;
@@ -2801,6 +2808,97 @@ impl<'test> TestCx<'test> {
28012808 println ! ( "init_incremental_test: incremental_dir={incremental_dir}" ) ;
28022809 }
28032810 }
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+ }
28042902}
28052903
28062904struct ProcArgs {
0 commit comments