2
2
3
3
use std:: borrow:: Cow ;
4
4
use std:: cell:: RefCell ;
5
+ use std:: ffi:: OsString ;
6
+ use std:: path:: PathBuf ;
5
7
use std:: sync:: OnceLock ;
6
8
use std:: { io, ops, str} ;
7
9
8
10
use regex:: Regex ;
9
- use rustc_graphviz as dot ;
11
+ use rustc_hir :: def_id :: DefId ;
10
12
use 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} ;
12
22
13
23
use super :: fmt:: { DebugDiffWithAdapter , DebugWithAdapter , DebugWithContext } ;
14
24
use 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
+ }
15
182
16
183
#[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
17
- pub ( crate ) enum OutputStyle {
184
+ enum OutputStyle {
18
185
AfterOnly ,
19
186
BeforeAndAfter ,
20
187
}
@@ -28,7 +195,7 @@ impl OutputStyle {
28
195
}
29
196
}
30
197
31
- pub ( crate ) struct Formatter < ' mir , ' tcx , A >
198
+ struct Formatter < ' mir , ' tcx , A >
32
199
where
33
200
A : Analysis < ' tcx > ,
34
201
{
@@ -45,12 +212,12 @@ impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A>
45
212
where
46
213
A : Analysis < ' tcx > ,
47
214
{
48
- pub ( crate ) fn new (
215
+ fn new (
49
216
body : & ' mir Body < ' tcx > ,
50
217
results : & ' mir mut Results < ' tcx , A > ,
51
218
style : OutputStyle ,
52
219
) -> Self {
53
- let reachable = mir :: traversal:: reachable_as_bitset ( body) ;
220
+ let reachable = traversal:: reachable_as_bitset ( body) ;
54
221
Formatter { cursor : results. as_results_cursor ( body) . into ( ) , style, reachable }
55
222
}
56
223
61
228
62
229
/// A pair of a basic block and an index into that basic blocks `successors`.
63
230
#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
64
- pub ( crate ) struct CfgEdge {
231
+ struct CfgEdge {
65
232
source : BasicBlock ,
66
233
index : usize ,
67
234
}
@@ -520,7 +687,7 @@ struct StateDiffCollector<D> {
520
687
521
688
impl < D > StateDiffCollector < D > {
522
689
fn run < ' tcx , A > (
523
- body : & mir :: Body < ' tcx > ,
690
+ body : & Body < ' tcx > ,
524
691
block : BasicBlock ,
525
692
results : & mut Results < ' tcx , A > ,
526
693
style : OutputStyle ,
0 commit comments