88use crate :: emitter:: FileWithAnnotatedLines ;
99use crate :: snippet:: Line ;
1010use crate :: { CodeSuggestion , Diagnostic , DiagnosticId , Emitter , Level , SubDiagnostic } ;
11- use annotate_snippets:: display_list:: DisplayList ;
12- use annotate_snippets:: formatter:: DisplayListFormatter ;
11+ use annotate_snippets:: display_list:: { DisplayList , FormatOptions } ;
1312use annotate_snippets:: snippet:: * ;
1413use rustc_data_structures:: sync:: Lrc ;
1514use rustc_span:: source_map:: SourceMap ;
16- use rustc_span:: { Loc , MultiSpan , SourceFile } ;
15+ use rustc_span:: { MultiSpan , SourceFile } ;
1716
1817/// Generates diagnostics using annotate-snippet
1918pub struct AnnotateSnippetEmitterWriter {
@@ -59,112 +58,20 @@ impl Emitter for AnnotateSnippetEmitterWriter {
5958 }
6059}
6160
62- /// Collects all the data needed to generate the data structures needed for the
63- /// `annotate-snippets` library.
64- struct DiagnosticConverter < ' a > {
65- source_map : Option < Lrc < SourceMap > > ,
66- level : Level ,
67- message : String ,
68- code : Option < DiagnosticId > ,
69- msp : MultiSpan ,
70- #[ allow( dead_code) ]
71- children : & ' a [ SubDiagnostic ] ,
72- #[ allow( dead_code) ]
73- suggestions : & ' a [ CodeSuggestion ] ,
61+ /// Provides the source string for the given `line` of `file`
62+ fn source_string ( file : Lrc < SourceFile > , line : & Line ) -> String {
63+ file. get_line ( line. line_index - 1 ) . map ( |a| a. to_string ( ) ) . unwrap_or_default ( )
7464}
7565
76- impl < ' a > DiagnosticConverter < ' a > {
77- /// Turns rustc Diagnostic information into a `annotate_snippets::snippet::Snippet`.
78- fn to_annotation_snippet ( & self ) -> Option < Snippet > {
79- if let Some ( source_map) = & self . source_map {
80- // Make sure our primary file comes first
81- let primary_lo = if let Some ( ref primary_span) = self . msp . primary_span ( ) . as_ref ( ) {
82- source_map. lookup_char_pos ( primary_span. lo ( ) )
83- } else {
84- // FIXME(#59346): Not sure when this is the case and what
85- // should be done if it happens
86- return None ;
87- } ;
88- let annotated_files =
89- FileWithAnnotatedLines :: collect_annotations ( & self . msp , & self . source_map ) ;
90- let slices = self . slices_for_files ( annotated_files, primary_lo) ;
91-
92- Some ( Snippet {
93- title : Some ( Annotation {
94- label : Some ( self . message . to_string ( ) ) ,
95- id : self . code . clone ( ) . map ( |c| match c {
96- DiagnosticId :: Error ( val) | DiagnosticId :: Lint ( val) => val,
97- } ) ,
98- annotation_type : Self :: annotation_type_for_level ( self . level ) ,
99- } ) ,
100- footer : vec ! [ ] ,
101- slices,
102- } )
103- } else {
104- // FIXME(#59346): Is it ok to return None if there's no source_map?
105- None
106- }
107- }
108-
109- fn slices_for_files (
110- & self ,
111- annotated_files : Vec < FileWithAnnotatedLines > ,
112- primary_lo : Loc ,
113- ) -> Vec < Slice > {
114- // FIXME(#64205): Provide a test case where `annotated_files` is > 1
115- annotated_files
116- . iter ( )
117- . flat_map ( |annotated_file| {
118- annotated_file
119- . lines
120- . iter ( )
121- . map ( |line| {
122- let line_source = Self :: source_string ( annotated_file. file . clone ( ) , & line) ;
123- Slice {
124- source : line_source,
125- line_start : line. line_index ,
126- origin : Some ( primary_lo. file . name . to_string ( ) ) ,
127- // FIXME(#59346): Not really sure when `fold` should be true or false
128- fold : false ,
129- annotations : line
130- . annotations
131- . iter ( )
132- . map ( |a| self . annotation_to_source_annotation ( a. clone ( ) ) )
133- . collect ( ) ,
134- }
135- } )
136- . collect :: < Vec < Slice > > ( )
137- } )
138- . collect :: < Vec < Slice > > ( )
139- }
140-
141- /// Turns a `crate::snippet::Annotation` into a `SourceAnnotation`
142- fn annotation_to_source_annotation (
143- & self ,
144- annotation : crate :: snippet:: Annotation ,
145- ) -> SourceAnnotation {
146- SourceAnnotation {
147- range : ( annotation. start_col , annotation. end_col ) ,
148- label : annotation. label . unwrap_or ( "" . to_string ( ) ) ,
149- annotation_type : Self :: annotation_type_for_level ( self . level ) ,
150- }
151- }
152-
153- /// Provides the source string for the given `line` of `file`
154- fn source_string ( file : Lrc < SourceFile > , line : & Line ) -> String {
155- file. get_line ( line. line_index - 1 ) . map ( |a| a. to_string ( ) ) . unwrap_or ( String :: new ( ) )
156- }
157-
158- /// Maps `Diagnostic::Level` to `snippet::AnnotationType`
159- fn annotation_type_for_level ( level : Level ) -> AnnotationType {
160- match level {
161- Level :: Bug | Level :: Fatal | Level :: Error => AnnotationType :: Error ,
162- Level :: Warning => AnnotationType :: Warning ,
163- Level :: Note => AnnotationType :: Note ,
164- Level :: Help => AnnotationType :: Help ,
165- // FIXME(#59346): Not sure how to map these two levels
166- Level :: Cancelled | Level :: FailureNote => AnnotationType :: Error ,
167- }
66+ /// Maps `Diagnostic::Level` to `snippet::AnnotationType`
67+ fn annotation_type_for_level ( level : Level ) -> AnnotationType {
68+ match level {
69+ Level :: Bug | Level :: Fatal | Level :: Error => AnnotationType :: Error ,
70+ Level :: Warning => AnnotationType :: Warning ,
71+ Level :: Note => AnnotationType :: Note ,
72+ Level :: Help => AnnotationType :: Help ,
73+ // FIXME(#59346): Not sure how to map these two levels
74+ Level :: Cancelled | Level :: FailureNote => AnnotationType :: Error ,
16875 }
16976}
17077
@@ -191,25 +98,87 @@ impl AnnotateSnippetEmitterWriter {
19198 message : String ,
19299 code : & Option < DiagnosticId > ,
193100 msp : & MultiSpan ,
194- children : & [ SubDiagnostic ] ,
195- suggestions : & [ CodeSuggestion ] ,
101+ _children : & [ SubDiagnostic ] ,
102+ _suggestions : & [ CodeSuggestion ] ,
196103 ) {
197- let converter = DiagnosticConverter {
198- source_map : self . source_map . clone ( ) ,
199- level : * level,
200- message,
201- code : code. clone ( ) ,
202- msp : msp. clone ( ) ,
203- children,
204- suggestions,
205- } ;
206- if let Some ( snippet) = converter. to_annotation_snippet ( ) {
207- let dl = DisplayList :: from ( snippet) ;
208- let dlf = DisplayListFormatter :: new ( true , self . ui_testing ) ;
104+ if let Some ( source_map) = & self . source_map {
105+ // Make sure our primary file comes first
106+ let primary_lo = if let Some ( ref primary_span) = msp. primary_span ( ) . as_ref ( ) {
107+ if primary_span. is_dummy ( ) {
108+ // FIXME(#59346): Not sure when this is the case and what
109+ // should be done if it happens
110+ return ;
111+ } else {
112+ source_map. lookup_char_pos ( primary_span. lo ( ) )
113+ }
114+ } else {
115+ // FIXME(#59346): Not sure when this is the case and what
116+ // should be done if it happens
117+ return ;
118+ } ;
119+ let mut annotated_files =
120+ FileWithAnnotatedLines :: collect_annotations ( msp, & self . source_map ) ;
121+ if let Ok ( pos) =
122+ annotated_files. binary_search_by ( |x| x. file . name . cmp ( & primary_lo. file . name ) )
123+ {
124+ annotated_files. swap ( 0 , pos) ;
125+ }
126+ // owned: line source, line index, annotations
127+ type Owned = ( String , usize , Vec < crate :: snippet:: Annotation > ) ;
128+ let origin = primary_lo. file . name . to_string ( ) ;
129+ let annotated_files: Vec < Owned > = annotated_files
130+ . into_iter ( )
131+ . flat_map ( |annotated_file| {
132+ let file = annotated_file. file ;
133+ annotated_file
134+ . lines
135+ . into_iter ( )
136+ . map ( |line| {
137+ ( source_string ( file. clone ( ) , & line) , line. line_index , line. annotations )
138+ } )
139+ . collect :: < Vec < Owned > > ( )
140+ } )
141+ . collect ( ) ;
142+ let snippet = Snippet {
143+ title : Some ( Annotation {
144+ label : Some ( & message) ,
145+ id : code. as_ref ( ) . map ( |c| match c {
146+ DiagnosticId :: Error ( val) | DiagnosticId :: Lint ( val) => val. as_str ( ) ,
147+ } ) ,
148+ annotation_type : annotation_type_for_level ( * level) ,
149+ } ) ,
150+ footer : vec ! [ ] ,
151+ opt : FormatOptions { color : true , anonymized_line_numbers : self . ui_testing } ,
152+ slices : annotated_files
153+ . iter ( )
154+ . map ( |( source, line_index, annotations) | {
155+ Slice {
156+ source,
157+ line_start : * line_index,
158+ origin : Some ( & origin) ,
159+ // FIXME(#59346): Not really sure when `fold` should be true or false
160+ fold : false ,
161+ annotations : annotations
162+ . into_iter ( )
163+ . map ( |annotation| SourceAnnotation {
164+ range : ( annotation. start_col , annotation. end_col ) ,
165+ label : annotation
166+ . label
167+ . as_ref ( )
168+ . map ( |s| s. as_str ( ) )
169+ . unwrap_or_default ( ) ,
170+ annotation_type : annotation_type_for_level ( * level) ,
171+ } )
172+ . collect ( ) ,
173+ }
174+ } )
175+ . collect ( ) ,
176+ } ;
209177 // FIXME(#59346): Figure out if we can _always_ print to stderr or not.
210178 // `emitter.rs` has the `Destination` enum that lists various possible output
211179 // destinations.
212- eprintln ! ( "{}" , dlf. format( & dl) ) ;
213- } ;
180+ eprintln ! ( "{}" , DisplayList :: from( snippet) )
181+ }
182+ // FIXME(#59346): Is it ok to return None if there's no source_map?
214183 }
215184}
0 commit comments