22
33use std:: borrow:: Cow ;
44use std:: cell:: RefCell ;
5+ use std:: ffi:: OsString ;
6+ use std:: path:: PathBuf ;
57use std:: sync:: OnceLock ;
68use std:: { io, ops, str} ;
79
810use regex:: Regex ;
9- use rustc_graphviz as dot ;
11+ use rustc_hir :: def_id :: DefId ;
1012use rustc_index:: bit_set:: BitSet ;
11- use rustc_middle:: mir:: { self , BasicBlock , Body , Location , graphviz_safe_def_name} ;
13+ use rustc_middle:: mir:: {
14+ self , BasicBlock , Body , Location , create_dump_file, dump_enabled, graphviz_safe_def_name,
15+ traversal,
16+ } ;
17+ use rustc_middle:: ty:: TyCtxt ;
18+ use rustc_middle:: ty:: print:: with_no_trimmed_paths;
19+ use rustc_span:: symbol:: { Symbol , sym} ;
20+ use tracing:: debug;
21+ use { rustc_ast as ast, rustc_graphviz as dot} ;
1222
1323use super :: fmt:: { DebugDiffWithAdapter , DebugWithAdapter , DebugWithContext } ;
1424use super :: { Analysis , CallReturnPlaces , Direction , Results , ResultsCursor , ResultsVisitor } ;
25+ use crate :: errors:: {
26+ DuplicateValuesFor , PathMustEndInFilename , RequiresAnArgument , UnknownFormatter ,
27+ } ;
28+
29+ /// Writes a DOT file containing the results of a dataflow analysis if the user requested it via
30+ /// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are
31+ /// the same.
32+ pub ( super ) fn write_graphviz_results < ' tcx , A > (
33+ tcx : TyCtxt < ' tcx > ,
34+ body : & Body < ' tcx > ,
35+ results : & mut Results < ' tcx , A > ,
36+ pass_name : Option < & ' static str > ,
37+ ) -> std:: io:: Result < ( ) >
38+ where
39+ A : Analysis < ' tcx > ,
40+ A :: Domain : DebugWithContext < A > ,
41+ {
42+ use std:: fs;
43+ use std:: io:: Write ;
44+
45+ let def_id = body. source . def_id ( ) ;
46+ let Ok ( attrs) = RustcMirAttrs :: parse ( tcx, def_id) else {
47+ // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse`
48+ return Ok ( ( ) ) ;
49+ } ;
50+
51+ let file = try {
52+ match attrs. output_path ( A :: NAME ) {
53+ Some ( path) => {
54+ debug ! ( "printing dataflow results for {:?} to {}" , def_id, path. display( ) ) ;
55+ if let Some ( parent) = path. parent ( ) {
56+ fs:: create_dir_all ( parent) ?;
57+ }
58+ fs:: File :: create_buffered ( & path) ?
59+ }
60+
61+ None if dump_enabled ( tcx, A :: NAME , def_id) => {
62+ create_dump_file ( tcx, "dot" , false , A :: NAME , & pass_name. unwrap_or ( "-----" ) , body) ?
63+ }
64+
65+ _ => return Ok ( ( ) ) ,
66+ }
67+ } ;
68+ let mut file = match file {
69+ Ok ( f) => f,
70+ Err ( e) => return Err ( e) ,
71+ } ;
72+
73+ let style = match attrs. formatter {
74+ Some ( sym:: two_phase) => OutputStyle :: BeforeAndAfter ,
75+ _ => OutputStyle :: AfterOnly ,
76+ } ;
77+
78+ let mut buf = Vec :: new ( ) ;
79+
80+ let graphviz = Formatter :: new ( body, results, style) ;
81+ let mut render_opts =
82+ vec ! [ dot:: RenderOption :: Fontname ( tcx. sess. opts. unstable_opts. graphviz_font. clone( ) ) ] ;
83+ if tcx. sess . opts . unstable_opts . graphviz_dark_mode {
84+ render_opts. push ( dot:: RenderOption :: DarkTheme ) ;
85+ }
86+ let r = with_no_trimmed_paths ! ( dot:: render_opts( & graphviz, & mut buf, & render_opts) ) ;
87+
88+ let lhs = try {
89+ r?;
90+ file. write_all ( & buf) ?;
91+ } ;
92+
93+ lhs
94+ }
95+
96+ #[ derive( Default ) ]
97+ struct RustcMirAttrs {
98+ basename_and_suffix : Option < PathBuf > ,
99+ formatter : Option < Symbol > ,
100+ }
101+
102+ impl RustcMirAttrs {
103+ fn parse ( tcx : TyCtxt < ' _ > , def_id : DefId ) -> Result < Self , ( ) > {
104+ let mut result = Ok ( ( ) ) ;
105+ let mut ret = RustcMirAttrs :: default ( ) ;
106+
107+ let rustc_mir_attrs = tcx
108+ . get_attrs ( def_id, sym:: rustc_mir)
109+ . flat_map ( |attr| attr. meta_item_list ( ) . into_iter ( ) . flat_map ( |v| v. into_iter ( ) ) ) ;
110+
111+ for attr in rustc_mir_attrs {
112+ let attr_result = if attr. has_name ( sym:: borrowck_graphviz_postflow) {
113+ Self :: set_field ( & mut ret. basename_and_suffix , tcx, & attr, |s| {
114+ let path = PathBuf :: from ( s. to_string ( ) ) ;
115+ match path. file_name ( ) {
116+ Some ( _) => Ok ( path) ,
117+ None => {
118+ tcx. dcx ( ) . emit_err ( PathMustEndInFilename { span : attr. span ( ) } ) ;
119+ Err ( ( ) )
120+ }
121+ }
122+ } )
123+ } else if attr. has_name ( sym:: borrowck_graphviz_format) {
124+ Self :: set_field ( & mut ret. formatter , tcx, & attr, |s| match s {
125+ sym:: gen_kill | sym:: two_phase => Ok ( s) ,
126+ _ => {
127+ tcx. dcx ( ) . emit_err ( UnknownFormatter { span : attr. span ( ) } ) ;
128+ Err ( ( ) )
129+ }
130+ } )
131+ } else {
132+ Ok ( ( ) )
133+ } ;
134+
135+ result = result. and ( attr_result) ;
136+ }
137+
138+ result. map ( |( ) | ret)
139+ }
140+
141+ fn set_field < T > (
142+ field : & mut Option < T > ,
143+ tcx : TyCtxt < ' _ > ,
144+ attr : & ast:: MetaItemInner ,
145+ mapper : impl FnOnce ( Symbol ) -> Result < T , ( ) > ,
146+ ) -> Result < ( ) , ( ) > {
147+ if field. is_some ( ) {
148+ tcx. dcx ( )
149+ . emit_err ( DuplicateValuesFor { span : attr. span ( ) , name : attr. name_or_empty ( ) } ) ;
150+
151+ return Err ( ( ) ) ;
152+ }
153+
154+ if let Some ( s) = attr. value_str ( ) {
155+ * field = Some ( mapper ( s) ?) ;
156+ Ok ( ( ) )
157+ } else {
158+ tcx. dcx ( )
159+ . emit_err ( RequiresAnArgument { span : attr. span ( ) , name : attr. name_or_empty ( ) } ) ;
160+ Err ( ( ) )
161+ }
162+ }
163+
164+ /// Returns the path where dataflow results should be written, or `None`
165+ /// `borrowck_graphviz_postflow` was not specified.
166+ ///
167+ /// This performs the following transformation to the argument of `borrowck_graphviz_postflow`:
168+ ///
169+ /// "path/suffix.dot" -> "path/analysis_name_suffix.dot"
170+ fn output_path ( & self , analysis_name : & str ) -> Option < PathBuf > {
171+ let mut ret = self . basename_and_suffix . as_ref ( ) . cloned ( ) ?;
172+ let suffix = ret. file_name ( ) . unwrap ( ) ; // Checked when parsing attrs
173+
174+ let mut file_name: OsString = analysis_name. into ( ) ;
175+ file_name. push ( "_" ) ;
176+ file_name. push ( suffix) ;
177+ ret. set_file_name ( file_name) ;
178+
179+ Some ( ret)
180+ }
181+ }
15182
16183#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
17- pub ( crate ) enum OutputStyle {
184+ enum OutputStyle {
18185 AfterOnly ,
19186 BeforeAndAfter ,
20187}
@@ -28,7 +195,7 @@ impl OutputStyle {
28195 }
29196}
30197
31- pub ( crate ) struct Formatter < ' mir , ' tcx , A >
198+ struct Formatter < ' mir , ' tcx , A >
32199where
33200 A : Analysis < ' tcx > ,
34201{
@@ -45,12 +212,12 @@ impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A>
45212where
46213 A : Analysis < ' tcx > ,
47214{
48- pub ( crate ) fn new (
215+ fn new (
49216 body : & ' mir Body < ' tcx > ,
50217 results : & ' mir mut Results < ' tcx , A > ,
51218 style : OutputStyle ,
52219 ) -> Self {
53- let reachable = mir :: traversal:: reachable_as_bitset ( body) ;
220+ let reachable = traversal:: reachable_as_bitset ( body) ;
54221 Formatter { cursor : results. as_results_cursor ( body) . into ( ) , style, reachable }
55222 }
56223
61228
62229/// A pair of a basic block and an index into that basic blocks `successors`.
63230#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
64- pub ( crate ) struct CfgEdge {
231+ struct CfgEdge {
65232 source : BasicBlock ,
66233 index : usize ,
67234}
@@ -520,7 +687,7 @@ struct StateDiffCollector<D> {
520687
521688impl < D > StateDiffCollector < D > {
522689 fn run < ' tcx , A > (
523- body : & mir :: Body < ' tcx > ,
690+ body : & Body < ' tcx > ,
524691 block : BasicBlock ,
525692 results : & mut Results < ' tcx , A > ,
526693 style : OutputStyle ,
0 commit comments