1
1
use rustc_ast as ast;
2
- use rustc_data_structures:: fx:: FxHashMap ;
2
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
3
3
use rustc_data_structures:: sync:: Lrc ;
4
4
use rustc_errors:: { ColorConfig , ErrorReported } ;
5
5
use rustc_hir as hir;
@@ -23,6 +23,8 @@ use std::panic;
23
23
use std:: path:: PathBuf ;
24
24
use std:: process:: { self , Command , Stdio } ;
25
25
use std:: str;
26
+ use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
27
+ use std:: sync:: { Arc , Mutex } ;
26
28
27
29
use crate :: clean:: Attributes ;
28
30
use crate :: config:: Options ;
@@ -103,8 +105,10 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
103
105
104
106
let mut test_args = options. test_args . clone ( ) ;
105
107
let display_warnings = options. display_warnings ;
108
+ let externs = options. externs . clone ( ) ;
109
+ let json_unused_externs = options. json_unused_externs ;
106
110
107
- let tests = interface:: run_compiler ( config, |compiler| {
111
+ let res = interface:: run_compiler ( config, |compiler| {
108
112
compiler. enter ( |queries| {
109
113
let lower_to_hir = queries. lower_to_hir ( ) ?;
110
114
@@ -147,12 +151,15 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
147
151
} ) ;
148
152
compiler. session ( ) . abort_if_errors ( ) ;
149
153
150
- let ret: Result < _ , ErrorReported > = Ok ( collector. tests ) ;
154
+ let unused_extern_reports = collector. unused_extern_reports . clone ( ) ;
155
+ let compiling_test_count = collector. compiling_test_count . load ( Ordering :: SeqCst ) ;
156
+ let ret: Result < _ , ErrorReported > =
157
+ Ok ( ( collector. tests , unused_extern_reports, compiling_test_count) ) ;
151
158
ret
152
159
} )
153
160
} ) ;
154
- let tests = match tests {
155
- Ok ( tests ) => tests ,
161
+ let ( tests, unused_extern_reports , compiling_test_count ) = match res {
162
+ Ok ( res ) => res ,
156
163
Err ( ErrorReported ) => return Err ( ErrorReported ) ,
157
164
} ;
158
165
@@ -164,6 +171,29 @@ crate fn run(options: Options) -> Result<(), ErrorReported> {
164
171
Some ( testing:: Options :: new ( ) . display_output ( display_warnings) ) ,
165
172
) ;
166
173
174
+ // Collect and warn about unused externs, but only if we've gotten
175
+ // reports for each doctest
176
+ if json_unused_externs {
177
+ let unused_extern_reports: Vec < _ > =
178
+ std:: mem:: take ( & mut unused_extern_reports. lock ( ) . unwrap ( ) ) ;
179
+ if unused_extern_reports. len ( ) == compiling_test_count {
180
+ let extern_names = externs. iter ( ) . map ( |( name, _) | name) . collect :: < FxHashSet < & String > > ( ) ;
181
+ let mut unused_extern_names = unused_extern_reports
182
+ . iter ( )
183
+ . map ( |uexts| uexts. unused_extern_names . iter ( ) . collect :: < FxHashSet < & String > > ( ) )
184
+ . fold ( extern_names, |uextsa, uextsb| {
185
+ uextsa. intersection ( & uextsb) . map ( |v| * v) . collect :: < FxHashSet < & String > > ( )
186
+ } )
187
+ . iter ( )
188
+ . map ( |v| ( * v) . clone ( ) )
189
+ . collect :: < Vec < String > > ( ) ;
190
+ unused_extern_names. sort ( ) ;
191
+ let unused_extern_json =
192
+ serde_json:: to_string ( & UnusedExterns { unused_extern_names } ) . unwrap ( ) ;
193
+ eprintln ! ( "{}" , unused_extern_json) ;
194
+ }
195
+ }
196
+
167
197
Ok ( ( ) )
168
198
}
169
199
@@ -233,6 +263,12 @@ impl DirState {
233
263
}
234
264
}
235
265
266
+ #[ derive( serde:: Serialize , serde:: Deserialize ) ]
267
+ struct UnusedExterns {
268
+ /// List of unused externs by their names.
269
+ unused_extern_names : Vec < String > ,
270
+ }
271
+
236
272
fn run_test (
237
273
test : & str ,
238
274
cratename : & str ,
@@ -251,6 +287,7 @@ fn run_test(
251
287
outdir : DirState ,
252
288
path : PathBuf ,
253
289
test_id : & str ,
290
+ report_unused_externs : impl Fn ( UnusedExterns ) ,
254
291
) -> Result < ( ) , TestFailure > {
255
292
let ( test, line_offset, supports_color) =
256
293
make_test ( test, Some ( cratename) , as_test_harness, opts, edition, Some ( test_id) ) ;
@@ -276,6 +313,11 @@ fn run_test(
276
313
if as_test_harness {
277
314
compiler. arg ( "--test" ) ;
278
315
}
316
+ if options. json_unused_externs && !compile_fail {
317
+ compiler. arg ( "--error-format=json" ) ;
318
+ compiler. arg ( "--json" ) . arg ( "unused-externs" ) ;
319
+ compiler. arg ( "-Z" ) . arg ( "unstable-options" ) ;
320
+ }
279
321
for lib_str in & options. lib_strs {
280
322
compiler. arg ( "-L" ) . arg ( & lib_str) ;
281
323
}
@@ -335,7 +377,26 @@ fn run_test(
335
377
eprint ! ( "{}" , self . 0 ) ;
336
378
}
337
379
}
338
- let out = str:: from_utf8 ( & output. stderr ) . unwrap ( ) ;
380
+ let mut out_lines = str:: from_utf8 ( & output. stderr )
381
+ . unwrap ( )
382
+ . lines ( )
383
+ . filter ( |l| {
384
+ if let Ok ( uext) = serde_json:: from_str :: < UnusedExterns > ( l) {
385
+ report_unused_externs ( uext) ;
386
+ false
387
+ } else {
388
+ true
389
+ }
390
+ } )
391
+ . collect :: < Vec < _ > > ( ) ;
392
+
393
+ // Add a \n to the end to properly terminate the last line,
394
+ // but only if there was output to be printed
395
+ if out_lines. len ( ) > 0 {
396
+ out_lines. push ( "" ) ;
397
+ }
398
+
399
+ let out = out_lines. join ( "\n " ) ;
339
400
let _bomb = Bomb ( & out) ;
340
401
match ( output. status . success ( ) , compile_fail) {
341
402
( true , true ) => {
@@ -719,6 +780,8 @@ crate struct Collector {
719
780
source_map : Option < Lrc < SourceMap > > ,
720
781
filename : Option < PathBuf > ,
721
782
visited_tests : FxHashMap < ( String , usize ) , usize > ,
783
+ unused_extern_reports : Arc < Mutex < Vec < UnusedExterns > > > ,
784
+ compiling_test_count : AtomicUsize ,
722
785
}
723
786
724
787
impl Collector {
@@ -743,6 +806,8 @@ impl Collector {
743
806
source_map,
744
807
filename,
745
808
visited_tests : FxHashMap :: default ( ) ,
809
+ unused_extern_reports : Default :: default ( ) ,
810
+ compiling_test_count : AtomicUsize :: new ( 0 ) ,
746
811
}
747
812
}
748
813
@@ -789,6 +854,10 @@ impl Tester for Collector {
789
854
let runtool_args = self . options . runtool_args . clone ( ) ;
790
855
let target = self . options . target . clone ( ) ;
791
856
let target_str = target. to_string ( ) ;
857
+ let unused_externs = self . unused_extern_reports . clone ( ) ;
858
+ if !config. compile_fail {
859
+ self . compiling_test_count . fetch_add ( 1 , Ordering :: SeqCst ) ;
860
+ }
792
861
793
862
// FIXME(#44940): if doctests ever support path remapping, then this filename
794
863
// needs to be the result of `SourceMap::span_to_unmapped_path`.
@@ -844,6 +913,9 @@ impl Tester for Collector {
844
913
test_type : testing:: TestType :: DocTest ,
845
914
} ,
846
915
testfn : testing:: DynTestFn ( box move || {
916
+ let report_unused_externs = |uext| {
917
+ unused_externs. lock ( ) . unwrap ( ) . push ( uext) ;
918
+ } ;
847
919
let res = run_test (
848
920
& test,
849
921
& cratename,
@@ -862,6 +934,7 @@ impl Tester for Collector {
862
934
outdir,
863
935
path,
864
936
& test_id,
937
+ report_unused_externs,
865
938
) ;
866
939
867
940
if let Err ( err) = res {
0 commit comments