@@ -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:: 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
} ) ;
@@ -985,6 +975,12 @@ impl IndividualTestOptions {
985
975
}
986
976
}
987
977
978
+ /// A doctest scraped from the code, ready to be turned into a runnable test.
979
+ enum ScrapedDoctest {
980
+ Rust ( RustDoctest ) ,
981
+ Markdown ( MdDoctest ) ,
982
+ }
983
+
988
984
pub ( crate ) trait DoctestVisitor {
989
985
fn visit_test ( & mut self , test : String , config : LangString , line : usize ) ;
990
986
fn get_line ( & self ) -> usize {
@@ -996,36 +992,9 @@ pub(crate) trait DoctestVisitor {
996
992
pub ( crate ) struct Collector {
997
993
pub ( crate ) tests : Vec < test:: TestDescAndFn > ,
998
994
999
- // The name of the test displayed to the user, separated by `::`.
1000
- //
1001
- // In tests from Rust source, this is the path to the item
1002
- // e.g., `["std", "vec", "Vec", "push"]`.
1003
- //
1004
- // In tests from a markdown file, this is the titles of all headers (h1~h6)
1005
- // of the sections that contain the code block, e.g., if the markdown file is
1006
- // written as:
1007
- //
1008
- // ``````markdown
1009
- // # Title
1010
- //
1011
- // ## Subtitle
1012
- //
1013
- // ```rust
1014
- // assert!(true);
1015
- // ```
1016
- // ``````
1017
- //
1018
- // the `names` vector of that test will be `["Title", "Subtitle"]`.
1019
- names : Vec < String > ,
1020
-
1021
995
rustdoc_options : RustdocOptions ,
1022
- use_headers : bool ,
1023
- enable_per_target_ignores : bool ,
1024
996
crate_name : String ,
1025
997
opts : GlobalTestOptions ,
1026
- position : Span ,
1027
- source_map : Option < Lrc < SourceMap > > ,
1028
- filename : Option < PathBuf > ,
1029
998
visited_tests : FxHashMap < ( String , usize ) , usize > ,
1030
999
unused_extern_reports : Arc < Mutex < Vec < UnusedExterns > > > ,
1031
1000
compiling_test_count : AtomicUsize ,
@@ -1036,74 +1005,48 @@ impl Collector {
1036
1005
pub ( crate ) fn new (
1037
1006
crate_name : String ,
1038
1007
rustdoc_options : RustdocOptions ,
1039
- use_headers : bool ,
1040
1008
opts : GlobalTestOptions ,
1041
- source_map : Option < Lrc < SourceMap > > ,
1042
- filename : Option < PathBuf > ,
1043
- enable_per_target_ignores : bool ,
1044
1009
arg_file : PathBuf ,
1045
1010
) -> Collector {
1046
1011
Collector {
1047
1012
tests : Vec :: new ( ) ,
1048
- names : Vec :: new ( ) ,
1049
1013
rustdoc_options,
1050
- use_headers,
1051
- enable_per_target_ignores,
1052
1014
crate_name,
1053
1015
opts,
1054
- position : DUMMY_SP ,
1055
- source_map,
1056
- filename,
1057
1016
visited_tests : FxHashMap :: default ( ) ,
1058
1017
unused_extern_reports : Default :: default ( ) ,
1059
1018
compiling_test_count : AtomicUsize :: new ( 0 ) ,
1060
1019
arg_file,
1061
1020
}
1062
1021
}
1063
1022
1064
- fn generate_name ( & self , line : usize , filename : & FileName ) -> String {
1065
- let mut item_path = self . names . join ( "::" ) ;
1023
+ fn generate_name ( & self , filename : & FileName , line : usize , logical_path : & [ String ] ) -> String {
1024
+ let mut item_path = logical_path . join ( "::" ) ;
1066
1025
item_path. retain ( |c| c != ' ' ) ;
1067
1026
if !item_path. is_empty ( ) {
1068
1027
item_path. push ( ' ' ) ;
1069
1028
}
1070
1029
format ! ( "{} - {item_path}(line {line})" , filename. prefer_local( ) )
1071
1030
}
1072
1031
1073
- pub ( crate ) fn set_position ( & mut self , position : Span ) {
1074
- self . position = position;
1075
- }
1076
-
1077
- fn get_filename ( & self ) -> FileName {
1078
- if let Some ( ref source_map) = self . source_map {
1079
- let filename = source_map. span_to_filename ( self . position ) ;
1080
- if let FileName :: Real ( ref filename) = filename
1081
- && let Ok ( cur_dir) = env:: current_dir ( )
1082
- && let Some ( local_path) = filename. local_path ( )
1083
- && let Ok ( path) = local_path. strip_prefix ( & cur_dir)
1084
- {
1085
- return path. to_owned ( ) . into ( ) ;
1032
+ fn add_test ( & mut self , test : ScrapedDoctest ) {
1033
+ let ( filename, line, logical_path, langstr, text) = match test {
1034
+ ScrapedDoctest :: Rust ( RustDoctest { filename, line, logical_path, langstr, text } ) => {
1035
+ ( filename, line, logical_path, langstr, text)
1086
1036
}
1087
- filename
1088
- } else if let Some ( ref filename) = self . filename {
1089
- filename. clone ( ) . into ( )
1090
- } else {
1091
- FileName :: Custom ( "input" . to_owned ( ) )
1092
- }
1093
- }
1094
- }
1037
+ ScrapedDoctest :: Markdown ( MdDoctest { filename, line, logical_path, langstr, text } ) => {
1038
+ ( filename, line, logical_path, langstr, text)
1039
+ }
1040
+ } ;
1095
1041
1096
- impl DoctestVisitor for Collector {
1097
- fn visit_test ( & mut self , test : String , config : LangString , line : usize ) {
1098
- let filename = self . get_filename ( ) ;
1099
- let name = self . generate_name ( line, & filename) ;
1042
+ let name = self . generate_name ( & filename, line, & logical_path) ;
1100
1043
let crate_name = self . crate_name . clone ( ) ;
1101
1044
let opts = self . opts . clone ( ) ;
1102
- let edition = config . edition . unwrap_or ( self . rustdoc_options . edition ) ;
1045
+ let edition = langstr . edition . unwrap_or ( self . rustdoc_options . edition ) ;
1103
1046
let target_str = self . rustdoc_options . target . to_string ( ) ;
1104
1047
let unused_externs = self . unused_extern_reports . clone ( ) ;
1105
- let no_run = config . no_run || self . rustdoc_options . no_run ;
1106
- if !config . compile_fail {
1048
+ let no_run = langstr . no_run || self . rustdoc_options . no_run ;
1049
+ if !langstr . compile_fail {
1107
1050
self . compiling_test_count . fetch_add ( 1 , Ordering :: SeqCst ) ;
1108
1051
}
1109
1052
@@ -1140,11 +1083,11 @@ impl DoctestVisitor for Collector {
1140
1083
let rustdoc_test_options =
1141
1084
IndividualTestOptions :: new ( & self . rustdoc_options , & self . arg_file , test_id) ;
1142
1085
1143
- debug ! ( "creating test {name}: {test }" ) ;
1086
+ debug ! ( "creating test {name}: {text }" ) ;
1144
1087
self . tests . push ( test:: TestDescAndFn {
1145
1088
desc : test:: TestDesc {
1146
1089
name : test:: DynTestName ( name) ,
1147
- ignore : match config . ignore {
1090
+ ignore : match langstr . ignore {
1148
1091
Ignore :: All => true ,
1149
1092
Ignore :: None => false ,
1150
1093
Ignore :: Some ( ref ignores) => ignores. iter ( ) . any ( |s| target_str. contains ( s) ) ,
@@ -1157,7 +1100,7 @@ impl DoctestVisitor for Collector {
1157
1100
end_col : 0 ,
1158
1101
// compiler failures are test failures
1159
1102
should_panic : test:: ShouldPanic :: No ,
1160
- compile_fail : config . compile_fail ,
1103
+ compile_fail : langstr . compile_fail ,
1161
1104
no_run,
1162
1105
test_type : test:: TestType :: DocTest ,
1163
1106
} ,
@@ -1166,11 +1109,11 @@ impl DoctestVisitor for Collector {
1166
1109
unused_externs. lock ( ) . unwrap ( ) . push ( uext) ;
1167
1110
} ;
1168
1111
let res = run_test (
1169
- & test ,
1112
+ & text ,
1170
1113
& crate_name,
1171
1114
line,
1172
1115
rustdoc_test_options,
1173
- config ,
1116
+ langstr ,
1174
1117
no_run,
1175
1118
& opts,
1176
1119
edition,
@@ -1233,59 +1176,6 @@ impl DoctestVisitor for Collector {
1233
1176
} ) ) ,
1234
1177
} ) ;
1235
1178
}
1236
-
1237
- fn get_line ( & self ) -> usize {
1238
- if let Some ( ref source_map) = self . source_map {
1239
- let line = self . position . lo ( ) . to_usize ( ) ;
1240
- let line = source_map. lookup_char_pos ( BytePos ( line as u32 ) ) . line ;
1241
- if line > 0 { line - 1 } else { line }
1242
- } else {
1243
- 0
1244
- }
1245
- }
1246
-
1247
- fn visit_header ( & mut self , name : & str , level : u32 ) {
1248
- if self . use_headers {
1249
- // We use these headings as test names, so it's good if
1250
- // they're valid identifiers.
1251
- let name = name
1252
- . chars ( )
1253
- . enumerate ( )
1254
- . map ( |( i, c) | {
1255
- if ( i == 0 && rustc_lexer:: is_id_start ( c) )
1256
- || ( i != 0 && rustc_lexer:: is_id_continue ( c) )
1257
- {
1258
- c
1259
- } else {
1260
- '_'
1261
- }
1262
- } )
1263
- . collect :: < String > ( ) ;
1264
-
1265
- // Here we try to efficiently assemble the header titles into the
1266
- // test name in the form of `h1::h2::h3::h4::h5::h6`.
1267
- //
1268
- // Suppose that originally `self.names` contains `[h1, h2, h3]`...
1269
- let level = level as usize ;
1270
- if level <= self . names . len ( ) {
1271
- // ... Consider `level == 2`. All headers in the lower levels
1272
- // are irrelevant in this new level. So we should reset
1273
- // `self.names` to contain headers until <h2>, and replace that
1274
- // slot with the new name: `[h1, name]`.
1275
- self . names . truncate ( level) ;
1276
- self . names [ level - 1 ] = name;
1277
- } else {
1278
- // ... On the other hand, consider `level == 5`. This means we
1279
- // need to extend `self.names` to contain five headers. We fill
1280
- // in the missing level (<h4>) with `_`. Thus `self.names` will
1281
- // become `[h1, h2, h3, "_", name]`.
1282
- if level - 1 > self . names . len ( ) {
1283
- self . names . resize ( level - 1 , "_" . to_owned ( ) ) ;
1284
- }
1285
- self . names . push ( name) ;
1286
- }
1287
- }
1288
- }
1289
1179
}
1290
1180
1291
1181
#[ cfg( test) ] // used in tests
0 commit comments