@@ -8,7 +8,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8
8
use rustc_data_structures:: sync:: Lrc ;
9
9
use rustc_errors:: emitter:: stderr_destination;
10
10
use rustc_errors:: { ColorConfig , ErrorGuaranteed , FatalError } ;
11
- use rustc_hir:: def_id:: { CRATE_DEF_ID , LOCAL_CRATE } ;
11
+ use rustc_hir:: def_id:: LOCAL_CRATE ;
12
12
use rustc_hir:: CRATE_HIR_ID ;
13
13
use rustc_interface:: interface;
14
14
use rustc_parse:: maybe_new_parser_from_source_str;
@@ -19,10 +19,9 @@ use rustc_session::parse::ParseSess;
19
19
use rustc_span:: edition:: Edition ;
20
20
use rustc_span:: source_map:: SourceMap ;
21
21
use rustc_span:: symbol:: sym;
22
- use rustc_span:: { BytePos , FileName , Pos , Span , DUMMY_SP } ;
22
+ use rustc_span:: FileName ;
23
23
use rustc_target:: spec:: { Target , TargetTriple } ;
24
24
25
- use std:: env;
26
25
use std:: fs:: File ;
27
26
use std:: io:: { self , Write } ;
28
27
use std:: panic;
@@ -38,7 +37,8 @@ use crate::config::Options as RustdocOptions;
38
37
use crate :: html:: markdown:: { ErrorCodes , Ignore , LangString } ;
39
38
use crate :: lint:: init_lints;
40
39
41
- use self :: rust:: HirCollector ;
40
+ use self :: markdown:: MdDoctest ;
41
+ use self :: rust:: { HirCollector , RustDoctest } ;
42
42
43
43
/// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`).
44
44
#[ derive( Clone , Default ) ]
@@ -182,29 +182,19 @@ pub(crate) fn run(
182
182
let mut collector = Collector :: new (
183
183
tcx. crate_name ( LOCAL_CRATE ) . to_string ( ) ,
184
184
options,
185
- false ,
186
185
opts,
187
- Some ( compiler. sess . psess . clone_source_map ( ) ) ,
188
- None ,
189
- enable_per_target_ignores,
190
186
file_path,
191
187
) ;
192
188
193
- let mut hir_collector = HirCollector {
194
- sess : & compiler. sess ,
195
- collector : & mut collector,
196
- map : tcx. hir ( ) ,
197
- codes : ErrorCodes :: from (
198
- compiler. sess . opts . unstable_features . is_nightly_build ( ) ,
199
- ) ,
189
+ let hir_collector = HirCollector :: new (
190
+ & compiler. sess ,
191
+ tcx. hir ( ) ,
192
+ ErrorCodes :: from ( compiler. sess . opts . unstable_features . is_nightly_build ( ) ) ,
193
+ enable_per_target_ignores,
200
194
tcx,
201
- } ;
202
- hir_collector. visit_testable (
203
- "" . to_string ( ) ,
204
- CRATE_DEF_ID ,
205
- tcx. hir ( ) . span ( CRATE_HIR_ID ) ,
206
- |this| tcx. hir ( ) . walk_toplevel_module ( this) ,
207
195
) ;
196
+ let tests = hir_collector. collect_crate ( ) ;
197
+ tests. into_iter ( ) . for_each ( |t| collector. add_test ( ScrapedDoctest :: Rust ( t) ) ) ;
208
198
209
199
collector
210
200
} ) ;
@@ -986,6 +976,12 @@ impl IndividualTestOptions {
986
976
}
987
977
}
988
978
979
+ /// A doctest scraped from the code, ready to be turned into a runnable test.
980
+ enum ScrapedDoctest {
981
+ Rust ( RustDoctest ) ,
982
+ Markdown ( MdDoctest ) ,
983
+ }
984
+
989
985
pub ( crate ) trait DoctestVisitor {
990
986
fn visit_test ( & mut self , test : String , config : LangString , line : usize ) ;
991
987
fn get_line ( & self ) -> usize {
@@ -997,36 +993,9 @@ pub(crate) trait DoctestVisitor {
997
993
pub ( crate ) struct Collector {
998
994
pub ( crate ) tests : Vec < test:: TestDescAndFn > ,
999
995
1000
- // The name of the test displayed to the user, separated by `::`.
1001
- //
1002
- // In tests from Rust source, this is the path to the item
1003
- // e.g., `["std", "vec", "Vec", "push"]`.
1004
- //
1005
- // In tests from a markdown file, this is the titles of all headers (h1~h6)
1006
- // of the sections that contain the code block, e.g., if the markdown file is
1007
- // written as:
1008
- //
1009
- // ``````markdown
1010
- // # Title
1011
- //
1012
- // ## Subtitle
1013
- //
1014
- // ```rust
1015
- // assert!(true);
1016
- // ```
1017
- // ``````
1018
- //
1019
- // the `names` vector of that test will be `["Title", "Subtitle"]`.
1020
- names : Vec < String > ,
1021
-
1022
996
rustdoc_options : RustdocOptions ,
1023
- use_headers : bool ,
1024
- enable_per_target_ignores : bool ,
1025
997
crate_name : String ,
1026
998
opts : GlobalTestOptions ,
1027
- position : Span ,
1028
- source_map : Option < Lrc < SourceMap > > ,
1029
- filename : Option < PathBuf > ,
1030
999
visited_tests : FxHashMap < ( String , usize ) , usize > ,
1031
1000
unused_extern_reports : Arc < Mutex < Vec < UnusedExterns > > > ,
1032
1001
compiling_test_count : AtomicUsize ,
@@ -1037,74 +1006,48 @@ impl Collector {
1037
1006
pub ( crate ) fn new (
1038
1007
crate_name : String ,
1039
1008
rustdoc_options : RustdocOptions ,
1040
- use_headers : bool ,
1041
1009
opts : GlobalTestOptions ,
1042
- source_map : Option < Lrc < SourceMap > > ,
1043
- filename : Option < PathBuf > ,
1044
- enable_per_target_ignores : bool ,
1045
1010
arg_file : PathBuf ,
1046
1011
) -> Collector {
1047
1012
Collector {
1048
1013
tests : Vec :: new ( ) ,
1049
- names : Vec :: new ( ) ,
1050
1014
rustdoc_options,
1051
- use_headers,
1052
- enable_per_target_ignores,
1053
1015
crate_name,
1054
1016
opts,
1055
- position : DUMMY_SP ,
1056
- source_map,
1057
- filename,
1058
1017
visited_tests : FxHashMap :: default ( ) ,
1059
1018
unused_extern_reports : Default :: default ( ) ,
1060
1019
compiling_test_count : AtomicUsize :: new ( 0 ) ,
1061
1020
arg_file,
1062
1021
}
1063
1022
}
1064
1023
1065
- fn generate_name ( & self , line : usize , filename : & FileName ) -> String {
1066
- let mut item_path = self . names . join ( "::" ) ;
1024
+ fn generate_name ( & self , filename : & FileName , line : usize , logical_path : & [ String ] ) -> String {
1025
+ let mut item_path = logical_path . join ( "::" ) ;
1067
1026
item_path. retain ( |c| c != ' ' ) ;
1068
1027
if !item_path. is_empty ( ) {
1069
1028
item_path. push ( ' ' ) ;
1070
1029
}
1071
1030
format ! ( "{} - {item_path}(line {line})" , filename. prefer_local( ) )
1072
1031
}
1073
1032
1074
- pub ( crate ) fn set_position ( & mut self , position : Span ) {
1075
- self . position = position;
1076
- }
1077
-
1078
- fn get_filename ( & self ) -> FileName {
1079
- if let Some ( ref source_map) = self . source_map {
1080
- let filename = source_map. span_to_filename ( self . position ) ;
1081
- if let FileName :: Real ( ref filename) = filename
1082
- && let Ok ( cur_dir) = env:: current_dir ( )
1083
- && let Some ( local_path) = filename. local_path ( )
1084
- && let Ok ( path) = local_path. strip_prefix ( & cur_dir)
1085
- {
1086
- return path. to_owned ( ) . into ( ) ;
1033
+ fn add_test ( & mut self , test : ScrapedDoctest ) {
1034
+ let ( filename, line, logical_path, langstr, text) = match test {
1035
+ ScrapedDoctest :: Rust ( RustDoctest { filename, line, logical_path, langstr, text } ) => {
1036
+ ( filename, line, logical_path, langstr, text)
1087
1037
}
1088
- filename
1089
- } else if let Some ( ref filename) = self . filename {
1090
- filename. clone ( ) . into ( )
1091
- } else {
1092
- FileName :: Custom ( "input" . to_owned ( ) )
1093
- }
1094
- }
1095
- }
1038
+ ScrapedDoctest :: Markdown ( MdDoctest { filename, line, logical_path, langstr, text } ) => {
1039
+ ( filename, line, logical_path, langstr, text)
1040
+ }
1041
+ } ;
1096
1042
1097
- impl DoctestVisitor for Collector {
1098
- fn visit_test ( & mut self , test : String , config : LangString , line : usize ) {
1099
- let filename = self . get_filename ( ) ;
1100
- let name = self . generate_name ( line, & filename) ;
1043
+ let name = self . generate_name ( & filename, line, & logical_path) ;
1101
1044
let crate_name = self . crate_name . clone ( ) ;
1102
1045
let opts = self . opts . clone ( ) ;
1103
- let edition = config . edition . unwrap_or ( self . rustdoc_options . edition ) ;
1046
+ let edition = langstr . edition . unwrap_or ( self . rustdoc_options . edition ) ;
1104
1047
let target_str = self . rustdoc_options . target . to_string ( ) ;
1105
1048
let unused_externs = self . unused_extern_reports . clone ( ) ;
1106
- let no_run = config . no_run || self . rustdoc_options . no_run ;
1107
- if !config . compile_fail {
1049
+ let no_run = langstr . no_run || self . rustdoc_options . no_run ;
1050
+ if !langstr . compile_fail {
1108
1051
self . compiling_test_count . fetch_add ( 1 , Ordering :: SeqCst ) ;
1109
1052
}
1110
1053
@@ -1141,11 +1084,11 @@ impl DoctestVisitor for Collector {
1141
1084
let rustdoc_test_options =
1142
1085
IndividualTestOptions :: new ( & self . rustdoc_options , & self . arg_file , test_id) ;
1143
1086
1144
- debug ! ( "creating test {name}: {test }" ) ;
1087
+ debug ! ( "creating test {name}: {text }" ) ;
1145
1088
self . tests . push ( test:: TestDescAndFn {
1146
1089
desc : test:: TestDesc {
1147
1090
name : test:: DynTestName ( name) ,
1148
- ignore : match config . ignore {
1091
+ ignore : match langstr . ignore {
1149
1092
Ignore :: All => true ,
1150
1093
Ignore :: None => false ,
1151
1094
Ignore :: Some ( ref ignores) => ignores. iter ( ) . any ( |s| target_str. contains ( s) ) ,
@@ -1158,7 +1101,7 @@ impl DoctestVisitor for Collector {
1158
1101
end_col : 0 ,
1159
1102
// compiler failures are test failures
1160
1103
should_panic : test:: ShouldPanic :: No ,
1161
- compile_fail : config . compile_fail ,
1104
+ compile_fail : langstr . compile_fail ,
1162
1105
no_run,
1163
1106
test_type : test:: TestType :: DocTest ,
1164
1107
} ,
@@ -1167,11 +1110,11 @@ impl DoctestVisitor for Collector {
1167
1110
unused_externs. lock ( ) . unwrap ( ) . push ( uext) ;
1168
1111
} ;
1169
1112
let res = run_test (
1170
- & test ,
1113
+ & text ,
1171
1114
& crate_name,
1172
1115
line,
1173
1116
rustdoc_test_options,
1174
- config ,
1117
+ langstr ,
1175
1118
no_run,
1176
1119
& opts,
1177
1120
edition,
@@ -1234,59 +1177,6 @@ impl DoctestVisitor for Collector {
1234
1177
} ) ) ,
1235
1178
} ) ;
1236
1179
}
1237
-
1238
- fn get_line ( & self ) -> usize {
1239
- if let Some ( ref source_map) = self . source_map {
1240
- let line = self . position . lo ( ) . to_usize ( ) ;
1241
- let line = source_map. lookup_char_pos ( BytePos ( line as u32 ) ) . line ;
1242
- if line > 0 { line - 1 } else { line }
1243
- } else {
1244
- 0
1245
- }
1246
- }
1247
-
1248
- fn visit_header ( & mut self , name : & str , level : u32 ) {
1249
- if self . use_headers {
1250
- // We use these headings as test names, so it's good if
1251
- // they're valid identifiers.
1252
- let name = name
1253
- . chars ( )
1254
- . enumerate ( )
1255
- . map ( |( i, c) | {
1256
- if ( i == 0 && rustc_lexer:: is_id_start ( c) )
1257
- || ( i != 0 && rustc_lexer:: is_id_continue ( c) )
1258
- {
1259
- c
1260
- } else {
1261
- '_'
1262
- }
1263
- } )
1264
- . collect :: < String > ( ) ;
1265
-
1266
- // Here we try to efficiently assemble the header titles into the
1267
- // test name in the form of `h1::h2::h3::h4::h5::h6`.
1268
- //
1269
- // Suppose that originally `self.names` contains `[h1, h2, h3]`...
1270
- let level = level as usize ;
1271
- if level <= self . names . len ( ) {
1272
- // ... Consider `level == 2`. All headers in the lower levels
1273
- // are irrelevant in this new level. So we should reset
1274
- // `self.names` to contain headers until <h2>, and replace that
1275
- // slot with the new name: `[h1, name]`.
1276
- self . names . truncate ( level) ;
1277
- self . names [ level - 1 ] = name;
1278
- } else {
1279
- // ... On the other hand, consider `level == 5`. This means we
1280
- // need to extend `self.names` to contain five headers. We fill
1281
- // in the missing level (<h4>) with `_`. Thus `self.names` will
1282
- // become `[h1, h2, h3, "_", name]`.
1283
- if level - 1 > self . names . len ( ) {
1284
- self . names . resize ( level - 1 , "_" . to_owned ( ) ) ;
1285
- }
1286
- self . names . push ( name) ;
1287
- }
1288
- }
1289
- }
1290
1180
}
1291
1181
1292
1182
#[ cfg( test) ] // used in tests
0 commit comments